Snap for 6439596 from 6ea9b8b580f56ddac47dca63e8c94e23eb2edcb4 to qt-aml-tzdata-release

Change-Id: I1344ae21d1911e545304725a2a0f3a5528bd4fb1
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c84bd24..ebe5f4a 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -80,13 +80,3 @@
 $(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)
-$(call add-clean-step, find $(PRODUCT_OUT) -type l -name "charger" -print0 | xargs -0 rm -f)
-$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/adbd)
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 51d5755..716378b 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,27 +4,21 @@
       "name": "adbd_test"
     },
     {
-      "name": "CtsInitTestCases"
-    },
-    {
       "name": "debuggerd_test"
     },
     {
-      "name": "CtsFsMgrTestCases"
+      "name": "fs_mgr_unit_test"
     },
     {
       "name": "fs_mgr_vendor_overlay_test"
     },
     {
+      "name": "init_tests"
+    },
+    {
       "name": "libbase_test"
     },
     {
-      "name": "libpackagelistparser_test"
-    },
-    {
-      "name": "libcutils_test"
-    },
-    {
       "name": "libprocinfo_test"
     },
     {
@@ -34,13 +28,6 @@
       "name": "memunreachable_test"
     },
     {
-      "name": "memunreachable_unit_test"
-    },
-    {
-      "name": "memunreachable_unit_test",
-      "host": true
-    },
-    {
       "name": "memunreachable_binder_test"
     },
     {
diff --git a/adb/Android.bp b/adb/Android.bp
index d605907..01e00dd 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -26,7 +26,6 @@
         "-Wvla",
         "-DADB_HOST=1",         // overridden by adbd_defaults
         "-DALLOW_ADBD_ROOT=0",  // overridden by adbd_defaults
-        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
     ],
     cpp_std: "experimental",
 
@@ -60,9 +59,6 @@
                 // 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",
             ],
@@ -124,8 +120,7 @@
     "adb_trace.cpp",
     "adb_unique_fd.cpp",
     "adb_utils.cpp",
-    "fdevent/fdevent.cpp",
-    "fdevent/fdevent_poll.cpp",
+    "fdevent.cpp",
     "services.cpp",
     "sockets.cpp",
     "socket_spec.cpp",
@@ -141,15 +136,11 @@
     "sysdeps/posix/network.cpp",
 ]
 
-libadb_linux_srcs = [
-    "fdevent/fdevent_epoll.cpp",
-]
-
 libadb_test_srcs = [
     "adb_io_test.cpp",
     "adb_listeners_test.cpp",
     "adb_utils_test.cpp",
-    "fdevent/fdevent_test.cpp",
+    "fdevent_test.cpp",
     "socket_spec_test.cpp",
     "socket_test.cpp",
     "sysdeps_test.cpp",
@@ -173,11 +164,12 @@
 
     target: {
         linux: {
-            srcs: ["client/usb_linux.cpp"] + libadb_linux_srcs,
+            srcs: ["client/usb_linux.cpp"],
         },
         darwin: {
             srcs: ["client/usb_osx.cpp"],
         },
+
         not_windows: {
             srcs: libadb_posix_srcs,
         },
@@ -224,7 +216,6 @@
     target: {
         windows: {
             enabled: true,
-            ldflags: ["-municode"],
             shared_libs: ["AdbWinApi"],
         },
     },
@@ -252,7 +243,7 @@
         "libbase",
         "libcutils",
         "libcrypto_utils",
-        "libcrypto_static",
+        "libcrypto",
         "libdiagnose_usb",
         "liblog",
         "libusb",
@@ -273,33 +264,22 @@
         "client/console.cpp",
         "client/adb_install.cpp",
         "client/line_printer.cpp",
-        "client/fastdeploy.cpp",
-        "client/fastdeploycallbacks.cpp",
         "shell_service_protocol.cpp",
     ],
 
-    generated_headers: [
-        "bin2c_fastdeployagent",
-        "bin2c_fastdeployagentscript"
-    ],
-
     static_libs: [
         "libadb_host",
-        "libandroidfw",
         "libbase",
         "libcutils",
         "libcrypto_utils",
         "libcrypto",
-        "libfastdeploy_host",
         "libdiagnose_usb",
         "liblog",
         "libmdnssd",
-        "libprotobuf-cpp-lite",
         "libusb",
         "libutils",
         "liblog",
-        "libziparchive",
-        "libz",
+        "libcutils",
     ],
 
     stl: "libc++_static",
@@ -309,6 +289,10 @@
     // will violate ODR
     shared_libs: [],
 
+    required: [
+        "deploypatchgenerator",
+    ],
+
     // Archive adb, adb.exe.
     dist: {
         targets: [
@@ -344,7 +328,7 @@
     // libminadbd wants both, as it's used to build native tests.
     compile_multilib: "both",
 
-    srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
+    srcs: libadb_srcs + libadb_posix_srcs + [
         "daemon/auth.cpp",
         "daemon/jdwp_service.cpp",
     ],
@@ -360,8 +344,6 @@
     ],
 
     shared_libs: [
-        "libadbconnection_server",
-        "libadbd_auth",
         "libasyncio",
         "libbase",
         "libcrypto",
@@ -414,8 +396,6 @@
     ],
 
     shared_libs: [
-        "libadbconnection_server",
-        "libadbd_auth",
         "libasyncio",
         "libbase",
         "libcrypto",
@@ -424,16 +404,34 @@
         "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",
             ],
         },
@@ -462,8 +460,6 @@
     ],
 
     shared_libs: [
-        "libadbconnection_server",
-        "libadbd_auth",
         "libadbd_services",
         "libasyncio",
         "libbase",
@@ -496,44 +492,17 @@
         keep_symbols: true,
     },
 
-    stl: "libc++_static",
-    static_libs: [
+    shared_libs: [
         "libadbd",
-        "libadbd_auth",
         "libadbd_services",
-        "libasyncio",
         "libbase",
         "libcap",
-        "libcrypto_utils",
+        "libcrypto",
         "libcutils",
-        "libdiagnose_usb",
         "liblog",
-        "libmdnssd",
         "libminijail",
         "libselinux",
     ],
-
-    shared_libs: [
-        "libadbconnection_server",
-        "libcrypto",
-    ],
-}
-
-phony {
-    name: "adbd_system_binaries",
-    required: [
-        "abb",
-        "libadbd_auth",
-        "reboot",
-        "set-verity-state",
-    ]
-}
-
-phony {
-    name: "adbd_system_binaries_recovery",
-    required: [
-        "reboot.recovery",
-    ],
 }
 
 cc_binary {
@@ -583,11 +552,11 @@
 
     static_libs: [
         "libadbd",
-        "libadbd_auth",
         "libbase",
+        "libbootloader_message",
         "libcutils",
         "libcrypto_utils",
-        "libcrypto_static",
+        "libcrypto",
         "libdiagnose_usb",
         "liblog",
         "libusb",
@@ -595,7 +564,6 @@
         "libselinux",
     ],
     test_suites: ["device-tests"],
-    require_root: true,
 }
 
 python_test_host {
@@ -636,131 +604,3 @@
         },
     },
 }
-
-// Note: using pipe for xxd to control the variable name generated
-// the default name used by xxd is the path to the input file.
-java_genrule {
-    name: "bin2c_fastdeployagent",
-    out: ["deployagent.inc"],
-    srcs: [":deployagent"],
-    cmd: "(echo 'unsigned char kDeployAgent[] = {' && xxd -i <$(in) && echo '};') > $(out)",
-}
-
-genrule {
-    name: "bin2c_fastdeployagentscript",
-    out: ["deployagentscript.inc"],
-    srcs: ["fastdeploy/deployagent/deployagent.sh"],
-    cmd: "(echo 'unsigned char kDeployAgentScript[] = {' && xxd -i <$(in) && echo '};') > $(out)",
-}
-
-cc_library_host_static {
-    name: "libfastdeploy_host",
-    defaults: ["adb_defaults"],
-    srcs: [
-        "fastdeploy/deploypatchgenerator/apk_archive.cpp",
-        "fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp",
-        "fastdeploy/deploypatchgenerator/patch_utils.cpp",
-        "fastdeploy/proto/ApkEntry.proto",
-    ],
-    static_libs: [
-        "libadb_host",
-        "libandroidfw",
-        "libbase",
-        "libcutils",
-        "libcrypto_utils",
-        "libcrypto",
-        "libdiagnose_usb",
-        "liblog",
-        "libmdnssd",
-        "libusb",
-        "libutils",
-        "libziparchive",
-        "libz",
-    ],
-    stl: "libc++_static",
-    proto: {
-        type: "lite",
-        export_proto_headers: true,
-    },
-    target: {
-        windows: {
-            enabled: true,
-            shared_libs: ["AdbWinApi"],
-        },
-    },
-}
-
-cc_test_host {
-    name: "fastdeploy_test",
-    defaults: ["adb_defaults"],
-    srcs: [
-        "fastdeploy/deploypatchgenerator/apk_archive_test.cpp",
-        "fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp",
-        "fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
-    ],
-    static_libs: [
-        "libadb_host",
-        "libandroidfw",
-        "libbase",
-        "libcutils",
-        "libcrypto_utils",
-        "libcrypto",
-        "libdiagnose_usb",
-        "libfastdeploy_host",
-        "liblog",
-        "libmdnssd",
-        "libprotobuf-cpp-lite",
-        "libusb",
-        "libutils",
-        "libziparchive",
-        "libz",
-    ],
-    target: {
-        windows: {
-            enabled: true,
-            shared_libs: ["AdbWinApi"],
-        },
-    },
-    data: [
-        "fastdeploy/testdata/rotating_cube-metadata-release.data",
-        "fastdeploy/testdata/rotating_cube-release.apk",
-        "fastdeploy/testdata/sample.apk",
-        "fastdeploy/testdata/sample.cd",
-    ],
-}
-
-prebuilt_etc {
-    name: "com.android.adbd.ld.config.txt",
-    src: "apex/ld.config.txt",
-    filename: "ld.config.txt",
-    installable: false,
-}
-
-apex {
-    name: "com.android.adbd",
-    manifest: "apex/apex_manifest.json",
-
-    binaries: ["adbd"],
-    prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"],
-
-    key: "com.android.adbd.key",
-    certificate: ":com.android.adbd.certificate",
-}
-
-apex_key {
-    name: "com.android.adbd.key",
-    public_key: "apex/com.android.adbd.avbpubkey",
-    private_key: "apex/com.android.adbd.pem",
-}
-
-android_app_certificate {
-    name: "com.android.adbd.certificate",
-    certificate: "apex/com.android.adbd",
-}
-
-prebuilt_etc {
-    name: "com.android.adbd.init.rc",
-    src: "apex/adbd.rc",
-    filename: "init.rc",
-    installable: false,
-}
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 9b663be..050ba49 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -300,7 +300,6 @@
     handle_online(t);
 #else
     if (!auth_required) {
-        LOG(INFO) << "authentication not required";
         handle_online(t);
         send_connect(t);
     } else {
@@ -1053,9 +1052,9 @@
         // 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:")) {
+        if (ConsumePrefix(&service, "tport:")) {
             legacy = false;
-            if (android::base::ConsumePrefix(&service, "serial:")) {
+            if (ConsumePrefix(&service, "serial:")) {
                 serial_storage = service;
                 serial = serial_storage.c_str();
             } else if (service == "usb") {
@@ -1069,7 +1068,7 @@
             // 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 (ConsumePrefix(&service, "transport-id:")) {
                 if (!ParseUint(&transport_id, service)) {
                     SendFail(reply_fd, "invalid transport id");
                     return HostRequestResult::Handled;
@@ -1080,7 +1079,7 @@
                 type = kTransportLocal;
             } else if (service == "transport-any") {
                 type = kTransportAny;
-            } else if (android::base::ConsumePrefix(&service, "transport:")) {
+            } else if (ConsumePrefix(&service, "transport:")) {
                 serial_storage = service;
                 serial = serial_storage.c_str();
             }
@@ -1132,9 +1131,7 @@
 
     if (service == "features") {
         std::string error;
-        atransport* t =
-                s->transport ? s->transport
-                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
             SendOkay(reply_fd, FeatureSetToString(t->features()));
         } else {
@@ -1193,9 +1190,7 @@
     // These always report "unknown" rather than the actual error, for scripts.
     if (service == "get-serialno") {
         std::string error;
-        atransport* t =
-                s->transport ? s->transport
-                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
@@ -1205,9 +1200,7 @@
     }
     if (service == "get-devpath") {
         std::string error;
-        atransport* t =
-                s->transport ? s->transport
-                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
@@ -1217,9 +1210,7 @@
     }
     if (service == "get-state") {
         std::string error;
-        atransport* t =
-                s->transport ? s->transport
-                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, t->connection_state_name());
         } else {
@@ -1229,7 +1220,7 @@
     }
 
     // Indicates a new emulator instance has started.
-    if (android::base::ConsumePrefix(&service, "emulator:")) {
+    if (ConsumePrefix(&service, "emulator:")) {
         unsigned int port;
         if (!ParseUint(&port, service)) {
           LOG(ERROR) << "received invalid port for emulator: " << service;
@@ -1243,9 +1234,7 @@
 
     if (service == "reconnect") {
         std::string response;
-        atransport* t = s->transport ? s->transport
-                                     : acquire_one_transport(type, serial, transport_id, nullptr,
-                                                             &response, true);
+        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
         if (t != nullptr) {
             kick_transport(t, true);
             response =
@@ -1257,15 +1246,12 @@
 
     // TODO: Switch handle_forward_request to string_view.
     std::string service_str(service);
-    auto transport_acquirer = [=](std::string* error) {
-        if (s->transport) {
-            return s->transport;
-        } else {
-            std::string error;
-            return acquire_one_transport(type, serial, transport_id, nullptr, &error);
-        }
-    };
-    if (handle_forward_request(service_str.c_str(), transport_acquirer, reply_fd)) {
+    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;
     }
 
diff --git a/adb/adb.h b/adb/adb.h
index c6cb06a..9324cee 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -26,7 +26,7 @@
 #include <android-base/macros.h>
 
 #include "adb_trace.h"
-#include "fdevent/fdevent.h"
+#include "fdevent.h"
 #include "socket.h"
 #include "types.h"
 #include "usb.h"
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index bdb8efa..f5cdcb5 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -34,7 +34,7 @@
 #include "adb_utils.h"
 #include "sysdeps.h"
 
-bool SendProtocolString(borrowed_fd fd, std::string_view s) {
+bool SendProtocolString(int fd, std::string_view s) {
     unsigned int length = s.size();
     if (length > MAX_PAYLOAD - 4) {
         errno = EMSGSIZE;
@@ -47,7 +47,7 @@
     return WriteFdExactly(fd, str);
 }
 
-bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error) {
+bool ReadProtocolString(int 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)");
@@ -65,57 +65,57 @@
     return true;
 }
 
-bool SendOkay(borrowed_fd fd) {
+bool SendOkay(int fd) {
     return WriteFdExactly(fd, "OKAY", 4);
 }
 
-bool SendFail(borrowed_fd fd, std::string_view reason) {
+bool SendFail(int fd, std::string_view reason) {
     return WriteFdExactly(fd, "FAIL", 4) && SendProtocolString(fd, reason);
 }
 
-bool ReadFdExactly(borrowed_fd fd, void* buf, size_t len) {
+bool ReadFdExactly(int fd, void* buf, size_t len) {
     char* p = reinterpret_cast<char*>(buf);
 
     size_t len0 = len;
 
-    D("readx: fd=%d wanted=%zu", fd.get(), len);
+    D("readx: fd=%d wanted=%zu", fd, 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.get(), errno, strerror(errno));
+            D("readx: fd=%d error %d: %s", fd, errno, strerror(errno));
             return false;
         } else {
-            D("readx: fd=%d disconnected", fd.get());
+            D("readx: fd=%d disconnected", fd);
             errno = 0;
             return false;
         }
     }
 
-    VLOG(RWX) << "readx: fd=" << fd.get() << " wanted=" << len0 << " got=" << (len0 - len) << " "
-              << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
+    VLOG(RWX) << "readx: fd=" << fd << " wanted=" << len0 << " got=" << (len0 - len)
+              << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
 
     return true;
 }
 
-bool WriteFdExactly(borrowed_fd fd, const void* buf, size_t len) {
+bool WriteFdExactly(int fd, const void* buf, size_t len) {
     const char* p = reinterpret_cast<const char*>(buf);
     int r;
 
-    VLOG(RWX) << "writex: fd=" << fd.get() << " len=" << len << " "
-              << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
+    VLOG(RWX) << "writex: fd=" << fd << " 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.get(), errno, strerror(errno));
+            D("writex: fd=%d error %d: %s", fd, errno, strerror(errno));
             if (errno == EAGAIN) {
                 std::this_thread::yield();
                 continue;
             } else if (errno == EPIPE) {
-                D("writex: fd=%d disconnected", fd.get());
+                D("writex: fd=%d disconnected", fd);
                 errno = 0;
                 return false;
             } else {
@@ -129,15 +129,15 @@
     return true;
 }
 
-bool WriteFdExactly(borrowed_fd fd, const char* str) {
+bool WriteFdExactly(int fd, const char* str) {
     return WriteFdExactly(fd, str, strlen(str));
 }
 
-bool WriteFdExactly(borrowed_fd fd, const std::string& str) {
+bool WriteFdExactly(int fd, const std::string& str) {
     return WriteFdExactly(fd, str.c_str(), str.size());
 }
 
-bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) {
+bool WriteFdFmt(int fd, const char* fmt, ...) {
     std::string str;
 
     va_list ap;
@@ -148,7 +148,7 @@
     return WriteFdExactly(fd, str);
 }
 
-bool ReadOrderlyShutdown(borrowed_fd fd) {
+bool ReadOrderlyShutdown(int fd) {
     char buf[16];
 
     // Only call this function if you're sure that the peer does
@@ -178,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.get() << ") unexpectedly read "
+        VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") 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 9628946..d6e65d8 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -25,16 +25,16 @@
 #include "adb_unique_fd.h"
 
 // Sends the protocol "OKAY" message.
-bool SendOkay(borrowed_fd fd);
+bool SendOkay(int fd);
 
 // Sends the protocol "FAIL" message, with the given failure reason.
-bool SendFail(borrowed_fd fd, std::string_view reason);
+bool SendFail(int fd, std::string_view reason);
 
 // Writes a protocol-format string; a four hex digit length followed by the string data.
-bool SendProtocolString(borrowed_fd fd, std::string_view s);
+bool SendProtocolString(int fd, std::string_view s);
 
 // Reads a protocol-format string; a four hex digit length followed by the string data.
-bool ReadProtocolString(borrowed_fd fd, std::string* s, std::string* error);
+bool ReadProtocolString(int fd, std::string* s, std::string* error);
 
 // Reads exactly len bytes from fd into buf.
 //
@@ -42,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(borrowed_fd fd, void* buf, size_t len);
+bool ReadFdExactly(int fd, void* buf, size_t len);
 
 // Given a client socket, wait for orderly/graceful shutdown. Call this:
 //
@@ -60,19 +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(borrowed_fd fd);
+bool ReadOrderlyShutdown(int 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(borrowed_fd fd, const void* buf, size_t len);
+bool WriteFdExactly(int fd, const void* buf, size_t len);
 
 // Same as above, but for strings.
-bool WriteFdExactly(borrowed_fd fd, const char* s);
-bool WriteFdExactly(borrowed_fd fd, const std::string& s);
+bool WriteFdExactly(int fd, const char* s);
+bool WriteFdExactly(int fd, const std::string& s);
 
 // Same as above, but formats the string to send.
-bool WriteFdFmt(borrowed_fd fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
+bool WriteFdFmt(int fd, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
 #endif /* ADB_IO_H */
diff --git a/adb/adb_listeners_test.cpp b/adb/adb_listeners_test.cpp
index a7e2dea..b697769 100644
--- a/adb/adb_listeners_test.cpp
+++ b/adb/adb_listeners_test.cpp
@@ -21,7 +21,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include "fdevent/fdevent.h"
+#include "fdevent.h"
 #include "sysdeps.h"
 #include "transport.h"
 
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
index b6c910a..d47213d 100644
--- a/adb/adb_unique_fd.h
+++ b/adb/adb_unique_fd.h
@@ -32,8 +32,6 @@
 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 d1910f1..9791769 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -234,15 +234,15 @@
 
 #if !defined(_WIN32)
 // Windows version provided in sysdeps_win32.cpp
-bool set_file_block_mode(borrowed_fd fd, bool block) {
-    int flags = fcntl(fd.get(), F_GETFL, 0);
+bool set_file_block_mode(int fd, bool block) {
+    int flags = fcntl(fd, F_GETFL, 0);
     if (flags == -1) {
-        PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd.get();
+        PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
         return false;
     }
     flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
-    if (fcntl(fd.get(), F_SETFL, flags) != 0) {
-        PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd.get() << ", flags " << flags;
+    if (fcntl(fd, F_SETFL, flags) != 0) {
+        PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
         return false;
     }
     return true;
@@ -320,10 +320,6 @@
 }
 
 std::string GetLogFilePath() {
-    // https://issuetracker.google.com/112588493
-    const char* path = getenv("ANDROID_ADB_LOG_PATH");
-    if (path) return path;
-
 #if defined(_WIN32)
     const char log_name[] = "adb.log";
     WCHAR temp_path[MAX_PATH];
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 66cba12..5800a62 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <charconv>
 #include <condition_variable>
 #include <mutex>
 #include <string>
@@ -27,7 +26,6 @@
 #include <android-base/macros.h>
 
 #include "adb.h"
-#include "adb_unique_fd.h"
 
 void close_stdin();
 
@@ -53,7 +51,7 @@
 [[noreturn]] void error_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
 [[noreturn]] void perror_exit(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
 
-bool set_file_block_mode(borrowed_fd fd, bool block);
+bool set_file_block_mode(int fd, bool block);
 
 // Given forward/reverse targets, returns true if they look sane. If an error is found, fills
 // |error| and returns false.
@@ -113,17 +111,41 @@
 // Base-10 stroll on a string_view.
 template <typename T>
 inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining = nullptr) {
-    T value;
-    const auto res = std::from_chars(str.begin(), str.end(), value);
-    if (res.ec != std::errc{}) {
+    if (str.empty() || !isdigit(str[0])) {
         return false;
     }
-    if (res.ptr != str.end() && !remaining) {
-        return false;
-    }
-    if (remaining) {
-        *remaining = std::string_view(res.ptr, str.end() - res.ptr);
+
+    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;
 }
+
+inline bool ConsumePrefix(std::string_view* str, std::string_view prefix) {
+  if (str->starts_with(prefix)) {
+    str->remove_prefix(prefix.size());
+    return true;
+  }
+  return false;
+}
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index cdca3aa..bd676c2 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -149,13 +149,13 @@
 TEST(adb_utils, set_file_block_mode) {
     unique_fd fd(adb_open("/dev/null", O_RDWR | O_APPEND));
     ASSERT_GE(fd, 0);
-    int flags = fcntl(fd.get(), F_GETFL, 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.get(), F_GETFL, 0);
+    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.get(), F_GETFL, 0);
+    new_flags = fcntl(fd, F_GETFL, 0);
     ASSERT_EQ(flags, new_flags);
 }
 #endif
diff --git a/adb/apex/adbd.rc b/adb/apex/adbd.rc
deleted file mode 100644
index 9cb072b..0000000
--- a/adb/apex/adbd.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service adbd /apex/com.android.adbd/bin/adbd --root_seclabel=u:r:su:s0
-    class core
-    socket adbd seqpacket 660 system system
-    disabled
-    override
-    seclabel u:r:adbd:s0
diff --git a/adb/apex/apex_manifest.json b/adb/apex/apex_manifest.json
deleted file mode 100644
index ff2df12..0000000
--- a/adb/apex/apex_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "name": "com.android.adbd",
-  "version": 1
-}
diff --git a/adb/apex/com.android.adbd.avbpubkey b/adb/apex/com.android.adbd.avbpubkey
deleted file mode 100644
index 06235bd..0000000
--- a/adb/apex/com.android.adbd.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/adb/apex/com.android.adbd.pem b/adb/apex/com.android.adbd.pem
deleted file mode 100644
index 2c9a860..0000000
--- a/adb/apex/com.android.adbd.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKQIBAAKCAgEAwUmO4l/ZdLhmBcBtpwDjih6z6bC7iZDPAVgnFVnYuYDRlVDA
-9OCDwv02Wwc/YCNzON7vt7JBk3o9wyJZpqY9HR1PUjk2DJa/wHtxbskmLcqsvcoh
-wZxmMkgx1mFyni/vQ0tCjjxYmDcnpoVmSntoPG4LBTZRwbgE2roYSuEi7q88Z9+t
-cFiQ5x7MqVTzUFsi1E+rpsxRaTt6Ly9DO71yR1gMTqONsSgmFm8f2HhUCiQzRh7H
-qLwk8eN5ZLPLVc1JBqo8swuH5pR9whR8HaYyQtK1VANRR9oVj3JpRXmyFUk8QjEn
-91I3sFV1lErdP1uh6xi6ewMBp+mQ+ccNFiNJs8PHVprzbEgX2ah45Tnge95ZwnkR
-V/5G/EwGBsggk/BcZjQyj0PExG6LmygR7lq8q4m9ODJj3cmNLZsZu8ukMBxf4Fim
-4/Y7lyaelW0FL+x3CR27wlIxLyIf/JfUNv/cFO/O2MHrDHYdHtCbvg8vpq1MZtDN
-+gJIkYQNUfBEtGS4SkH3WWfNet3bcL5yFx5IVdwCY+n635jPA1fvr1vcIiKnyGUm
-zNE+jMOZkgk6lPPuDwllAX0D8nYTm1eBMCTAWCePO0QlcFHCT9j1/xKbFbjt/xYI
-0pXuOc8/1n61F5ybzH/91cS66gqmYUAekUiP0osTIZ7idVFJMoqpc9m7+rECAwEA
-AQKCAgEAkjg9WU89SCk/NNavnQj1GUXEwOKr3JOppdC0MFi5tQuYgSaH8jfuNZIs
-joxbCzWGMt2j5wl4xkJRes7/lyxnSyEjIoaZNsjL4qb/1tlggn+yUhkZlEfmn98x
-pIYvmS+WBwhmHwfT1cLTwgtkqK/W2PA+cgD3tF6rfXQOcIcEUCBMyB/UKws1A0Kv
-fOIA9ycaoBZtOk+SvtL5ybwtVoIoc4ROOydLR1uiBJKoOrA8kzdzenZKgIFkSYDW
-ErJY/l3AAsTCCoiMlIh84ldw1VUm7JpOBnJECOEYMl5Q+PfpGmU+qqxZGaYe7syX
-mElSOl3tjdY1LF3H4Oi2fd5xLfAgDgQjXcawKRYpImEgbqNfEUHW4BE/uVp0hHn+
-W0tCq9hvWoizhjxVq7oEfpdCXJBH0bTg9h3Ho2nuJMHTrUVbSWPTqNJn1xOi4Oxl
-vWsD5qjOOVw1e0P1dtxQ+6a8+rCL8LDvIthQC9Wpt0yXduEi/vUWiMFx2VbcSpNn
-5PB9HK7vvCpR/k0IocaTKt80D3m2svJCnfrekRx/7n//x8imrvtvaYNpoToTSN0q
-hPOpTNc77R4aARJNXm4sVHzGs6HUXsJfODJdjFtTuaDHjLvRoXZi2wFUVWBvIaFg
-/4+PHXjsfMkY15KULKn3f7Xs7K6rmINAb853zti3Qkllv1EeYoECggEBAP9t1Jxe
-hLKnVrJ5jJ0zCT0/ez6qM5cQG8YvXbVICmoAOQ+/NV6qjPABg5j8FuNhpyr45OuJ
-m1oISLgZPVCbIvYx3oZS4ekWUp9Z7jlDGzsWiBCkEUFLRzDLQRUl4bQMI2SWM+vD
-RL9AAM+NHJQ8LJN7ASNdSQw9ZinNCSByCZ52QjPCfRON0OPY4l1FJKHHymzBNXpe
-R5e9a1o9KEIhd7j+3YX9y8SOVrbUe6U8me5LZ6RY+pLB+cA/UHcSQK23hYAkMcvL
-MQny6B57P6rquzFZDG/OUOZWzWub2FSYTTmiYSHPAuB15FyWShs7h7+wK8y2xrSM
-Lq3FWHxzR1OK2HkCggEBAMG4KsAU/lp9rQhNpdw2NQXqbDLgHy09BFMOOWhyp2/Z
-2lbDo9aP746Q56HAfRRgx5oAAtr3SxeN/R/uEJLYzzDU+SrG4TQO/TZ3DPZOAVYM
-oESWG/HXLN4Hw6j4iWt2NvqpnSVJrvYr6zar/QxRHOMwnUoUV3ugmzUkqFC/Nwmm
-nMGJbTQbEha8OyatfwejmhrCkbQMBiCk0AQmgLybUxs2ckGs5jibau7VqXVxly0f
-WkAsWE/qfybQl4oyBhGCFNObr3Co/PHTaD4ACFQQvaEEF4bTuh6wP+MIgJKxL8IB
-SkrKWO5PFbJWY5lacnNMe7ITrWy60HukLlJe5or5lfkCggEBAP3Rwghw1CRDrR9F
-Mbm0UWYPgwTOVN20ICVcRB40LEURW6KOOxaLG+oTVxXay1PAYkGNes2jvEBHIxvt
-2MQUpTVIcPvBuMPKbufykYtNZ+3bgfInVw4vI9sU3uOI9TPZLAJ0T7vkGpiBnUyh
-yNh0w0b6YDMoK8KB8Ndw67TWHUDd+wM8LNYVgpInnylX4ALzae+QPvgOX84laFwP
-kcXFRBcNDExt2uLDHuAnXYbhJYVqYN8rnDPhlbC4OdlYxfTZ/UtMrD769wwP2SER
-ED9jagirmHQx7Ko3b4GTJ/FINtUiyqqx7wXloLtwjMtq6IZPJfcTWXloI6qCBGAG
-ncYinuECggEAfZeiF8BEm3RpTz3QL3HxdHFkTqOhctnhSNuq+n2C8nBCLwhN21ic
-DkkB84txTFnmboBdWYsEYzQKDL5yflIUGeup00L3VKH3Jm2OuM0f7qLm8TCE04kW
-rKhKAO2JYmNVB7QZjsgzp6QXre1ZdLfNy7mD8Dg584vPtGecvCUMULR1YsBvTV3T
-n2vPyaan+dLmoTzN6/XzrwxLVLWFt0HYYoctEkk/RSn17PwXDm5jfbya7YoSg1Vb
-tFV+Oflul8FHMV35I0hcHYhbR/8LZz0nRBH8EsyIGUdZVB76BKDdfqEJgm2ntHEP
-dvytPAo4s2m9tFkvkZOYgOCTq5GdVDK2OQKCAQAsz+y9rDcqFciCESu4IHzmtckT
-0kwP2W5ds5hzUjbY0Y2AKTx2oHNOFak6WW5vxN0+OIn37SNK3RBStPWJiigut4R4
-rGrZM4pijm53s3cWzd0h8XyLGisl2zORu8gD2IQLkQf79F3lEZHGA+J0mkSHB85N
-IuqReFzL6cfOToNd+8WYjMgJcXmVuKiCV1FRK3jrqNpXO2cLtnhFvQMxRUAYU4j+
-2MIdBFVeMq5ftMMOBS21hM9cNLlOzoSN2HmK+ZkmrlTCK0G9lmF427/lqXTmWLed
-sspOUbOLfBOwxdCCc9JUxz5KggKWcDcTqX25M0mv09rpuCxIEg8lez1Ax0if
------END RSA PRIVATE KEY-----
diff --git a/adb/apex/com.android.adbd.pk8 b/adb/apex/com.android.adbd.pk8
deleted file mode 100644
index cdddc3f..0000000
--- a/adb/apex/com.android.adbd.pk8
+++ /dev/null
Binary files differ
diff --git a/adb/apex/com.android.adbd.x509.pem b/adb/apex/com.android.adbd.x509.pem
deleted file mode 100644
index bb85c1d..0000000
--- a/adb/apex/com.android.adbd.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGHzCCBAegAwIBAgIUW8npFHXBP+wsEAesGMBxaV7TScAwDQYJKoZIhvcNAQEL
-BQAwgZ0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
-DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
-b2lkMRkwFwYDVQQDDBBjb20uYW5kcm9pZC5hZGJkMSIwIAYJKoZIhvcNAQkBFhNh
-bmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MDgxNTE5MzkxM1oYDzQ3NTcwNzExMTkz
-OTEzWjCBnTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNV
-BAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNVBAsMB0Fu
-ZHJvaWQxGTAXBgNVBAMMEGNvbS5hbmRyb2lkLmFkYmQxIjAgBgkqhkiG9w0BCQEW
-E2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
-AoICAQC6zbUeWi5vNA6vCC4FBrJQ9re4UexP6TabsDYvWpFBoCluvMkT2ZRmpXMF
-W7EzQ5VmuUvZgLYVHuJmnvHIV3uaRc2VE1SV+spjWTRt+6DtsAN7irR5K66POWMp
-+tr5hASdQBVOJdebimsepy0pH6sXREvanrrFzkSM/2Ho0unlwWJ5Y4jcnvdkVHI5
-Ks0vifLmX4y5mYgv1dcXYWzyYx39f8HyePv0cjRhYXiIEYZ49KWU4MjryvQe/mAu
-MQuMp901BLps2W1+oKyPPA4DV69KUXgF66RFfsjjkJJ/CSeQGzTuez+UWzFk3Duc
-6MmbiL1LTki3vyyVtjW1rYFO2s+M6Pa5NZWHgA55uUxiJ987WPyK9lWnMsY6YeKa
-FDBfS1JUzXGPzVncgM7LLvzAEibLdhjII88NsJvzPoHK0SluSn+E7t7iGO1fTjkD
-Js94iUJAp8OQ4GwkcTVgtEAR+NXzownNjHJ6qpiq6tXRqXdBqSat/glf01AgNDtz
-9AGeW7Mz6FqTdOzg3U4lu77+CGd3SZTuQk8C8PUDNhqhQX5H2qhr90bakGaXuYfE
-rWFzIjrVdJIznV1BimOCay5HyyHab4FWlVhAvslEQb2BpHRyi2lhe0laupOpmN44
-LzfjFM18bi2GashIi2OQuYDyAeT5mGtR2g8mC7g44H6dH+wTfQIDAQABo1MwUTAd
-BgNVHQ4EFgQU7lyyxPO5SOOh9a5O0l4+RjckcgcwHwYDVR0jBBgwFoAU7lyyxPO5
-SOOh9a5O0l4+RjckcgcwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
-AgEAStsOy8bkbZg/Ygx47bPkLSz0cJIvATxTChUGOabkz+brLis88ABVWVP0IXps
-tlLlZR5cjXBJguE7GJXzKPWzQZuB8+YwcGHG6QDFpfdMeGrxPDhwNfGy236ArVnx
-K0v1IIxoZRZ0P7aubk3xwUAPgsmT5ayZCKu+dqlEy5B6ioKEsr7Y2RRT/8ifERNm
-cjS9AhcyWrp4R3cjy2iA/RpdsPFwE5ac3I+GtUB4D2up5aDMsy85i9t2/1kuTUaA
-9UHwGXCpcqP8f8BqeLzuxDzYkAvkntlNxbXn1cbn+dTRIOCBoDbtSeqtxhWooOUH
-RQROeRsB7iicdYJJRge0+WyR+216AKUSQPE6/rT0Ifr06ZRwi22/YyySpwuO3SNA
-+yWffh+f4h31Dz+p6pu8wjbMDkq4LnCWyjLwfF/yhvWhwwm5+KPAEhvJABeHQc+3
-cslOC9dlXJm9sPoUC7ghmUiFsCmN2hIzQrr2QoK0Obh0AGexOvOAw9cqtOdZQncB
-bqC8c4sVYScVxwDWkg0lNfRMC5boPjBsl7+M2CC1ukgVpXTyDOEjMWILrBXfYCDX
-unBH3kbKQOfL5RT0nE1Lkt1rn5qAWMJg4mvS4QuIurbRtEoj3QYQadF9md4qJXs0
-TvqvY8iEC4xrWU2SQn1K3PutXgaLP9/b6Cy1SBrhBX+AC5s=
------END CERTIFICATE-----
diff --git a/adb/apex/ld.config.txt b/adb/apex/ld.config.txt
deleted file mode 100644
index 85f9b29..0000000
--- a/adb/apex/ld.config.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Bionic loader config file for the adbd APEX.
-
-dir.adbd = /apex/com.android.adbd/bin/
-
-[adbd]
-additional.namespaces = platform,art,adbd
-
-namespace.default.isolated = true
-namespace.default.links = art,adbd,platform
-namespace.default.link.art.shared_libs = libadbconnection_server.so
-namespace.default.link.platform.allow_all_shared_libs = true
-namespace.default.link.adbd.allow_all_shared_libs = true
-
-###############################################################################
-# "adbd" APEX namespace
-###############################################################################
-namespace.adbd.isolated = true
-namespace.adbd.search.paths = /apex/com.android.adbd/${LIB}
-namespace.adbd.asan.search.paths = /apex/com.android.adbd/${LIB}
-namespace.adbd.links = platform
-namespace.adbd.link.platform.allow_all_shared_libs = true
-
-###############################################################################
-# "art" APEX namespace: used for libadbdconnection_server
-###############################################################################
-namespace.art.isolated = true
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = platform
-namespace.art.link.platform.allow_all_shared_libs = true
-
-###############################################################################
-# "platform" namespace: used for NDK libraries, and libadbd_auth
-###############################################################################
-namespace.platform.isolated = true
-namespace.platform.search.paths = /system/${LIB}
-namespace.platform.asan.search.paths = /data/asan/system/${LIB}
-
-# /system/lib/libc.so, etc are symlinks to
-# /apex/com.android.runtime/lib/bionic/libc.so, etc. Add the path to the
-# permitted paths because linker uses realpath(3) to check the accessibility
-# of the lib. We could add this to search.paths instead but that makes the
-# resolution of bionic libs be dependent on the order of /system/lib and
-# /apex/.../lib/bionic in search.paths. If the latter is after the former,
-# then the latter is never tried because libc.so is always found in
-# /system/lib but fails to pass the accessibility test because of its realpath.
-# It's better to not depend on the ordering if possible.
-namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
-namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index d91ae35..5a7bc8d 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -128,7 +128,7 @@
     return result;
 }
 
-bool adb_status(borrowed_fd fd, std::string* error) {
+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)");
@@ -149,8 +149,7 @@
     return false;
 }
 
-static int _adb_connect(std::string_view service, TransportId* transport, std::string* error,
-                        bool force_switch = 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());
@@ -165,7 +164,7 @@
         return -2;
     }
 
-    if (!service.starts_with("host") || force_switch) {
+    if (!service.starts_with("host")) {
         std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
         if (!transport_result) {
             return -1;
@@ -324,8 +323,7 @@
     return result;
 }
 
-int adb_connect(TransportId* transport, std::string_view service, std::string* error,
-                bool force_switch_device) {
+int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
     LOG(DEBUG) << "adb_connect: service: " << service;
 
     // Query the adb server's version.
@@ -338,7 +336,7 @@
         return 0;
     }
 
-    unique_fd fd(_adb_connect(service, transport, error, force_switch_device));
+    unique_fd fd(_adb_connect(service, transport, error));
     if (fd == -1) {
         D("_adb_connect error: %s", error->c_str());
     } else if(fd == -2) {
@@ -400,15 +398,9 @@
 }
 
 bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
-    static FeatureSet* features = nullptr;
-    if (!features) {
-        std::string result;
-        if (adb_query(format_host_command("features"), &result, error)) {
-            features = new FeatureSet(StringToFeatureSet(result));
-        }
-    }
-    if (features) {
-        *feature_set = *features;
+    std::string result;
+    if (adb_query(format_host_command("features"), &result, error)) {
+        *feature_set = StringToFeatureSet(result);
         return true;
     }
     feature_set->clear();
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index ba53041..8d32c93 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -16,14 +16,13 @@
 
 #pragma once
 
-#include <optional>
-#include <string>
-
 #include "adb.h"
-#include "adb_unique_fd.h"
 #include "sysdeps.h"
 #include "transport.h"
 
+#include <optional>
+#include <string>
+
 // 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.
@@ -34,11 +33,7 @@
 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.
-// force_switch_device forces the function to attempt to select a device, even if the service
-// string appears to be a host: service (for use with host services that are device specific, like
-// forward).
-int adb_connect(TransportId* _Nullable id, std::string_view service, std::string* _Nonnull error,
-                bool force_switch_device = false);
+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();
@@ -69,7 +64,7 @@
 
 // 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);
+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);
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 73dcde1..16fa215 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -16,7 +16,6 @@
 
 #include "adb_install.h"
 
-#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -37,33 +36,22 @@
 #include "commandline.h"
 #include "fastdeploy.h"
 
+#if defined(ENABLE_FASTDEPLOY)
 static constexpr int kFastDeployMinApi = 24;
-
-namespace {
-
-enum InstallMode {
-    INSTALL_DEFAULT,
-    INSTALL_PUSH,
-    INSTALL_STREAM,
-};
-
-}
+#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 false;
+        return true;
     }
     return CanUseFeature(features, feature);
 }
 
-static InstallMode best_install_mode() {
-    if (can_use_feature(kFeatureCmd)) {
-        return INSTALL_STREAM;
-    }
-    return INSTALL_PUSH;
+static bool use_legacy_install() {
+    return !can_use_feature(kFeatureCmd);
 }
 
 static bool is_apex_supported() {
@@ -124,7 +112,7 @@
 }
 
 int uninstall_app(int argc, const char** argv) {
-    if (best_install_mode() == INSTALL_PUSH) {
+    if (use_legacy_install()) {
         return uninstall_app_legacy(argc, argv);
     }
     return uninstall_app_streamed(argc, argv);
@@ -144,7 +132,15 @@
     *buf = '\0';
 }
 
-static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
+#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
@@ -166,80 +162,81 @@
         error_exit("--fastdeploy doesn't support .apex files");
     }
 
-    if (use_fastdeploy) {
-        auto metadata = extract_metadata(file);
-        if (metadata.has_value()) {
-            // 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};
-            auto patchFd = install_patch(pm_args.size(), pm_args.data());
-            return stream_patch(file, std::move(metadata.value()), std::move(patchFd));
+    if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
+        TemporaryFile metadataTmpFile;
+        std::string patchTmpFilePath;
+        {
+            TemporaryFile patchTmpFile;
+            patchTmpFile.DoNotRemove();
+            patchTmpFilePath = patchTmpFile.path;
         }
-    }
 
-    struct stat sb;
-    if (stat(file, &sb) == -1) {
-        fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-        return 1;
-    }
+        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
+        extract_metadata(file, metadataFile);
+        fclose(metadataFile);
 
-    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;
-    }
-
-#ifdef __linux__
-    posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
-#endif
-
-    const bool use_abb = can_use_feature(kFeatureAbbExec);
-    std::string error;
-    std::vector<std::string> cmd_args = {use_abb ? "package" : "exec:cmd package"};
-    cmd_args.reserve(argc + 3);
-
-    // don't copy the APK name, but, copy the rest of the arguments as-is
-    while (argc-- > 1) {
-        if (use_abb) {
-            cmd_args.push_back(*argv++);
-        } else {
-            cmd_args.push_back(escape_arg(*argv++));
-        }
-    }
-
-    // add size parameter [required for streaming installs]
-    // do last to override any user specified value
-    cmd_args.push_back("-S");
-    cmd_args.push_back(android::base::StringPrintf("%" PRIu64, static_cast<uint64_t>(sb.st_size)));
-
-    if (is_apex) {
-        cmd_args.push_back("--apex");
-    }
-
-    unique_fd remote_fd;
-    if (use_abb) {
-        remote_fd = send_abb_exec_command(cmd_args, &error);
-    } else {
-        remote_fd.reset(adb_connect(android::base::Join(cmd_args, " "), &error));
-    }
-    if (remote_fd < 0) {
-        fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-        return 1;
-    }
-
-    copy_to_file(local_fd.get(), remote_fd.get());
-
-    char buf[BUFSIZ];
-    read_status_line(remote_fd.get(), buf, sizeof(buf));
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
+        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;
     }
-    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) {
+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.
@@ -259,35 +256,50 @@
 
     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]);
+    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);
 
-    if (use_fastdeploy) {
-        auto metadata = extract_metadata(apk_file[0]);
-        if (metadata.has_value()) {
-            auto patchFd = apply_patch_on_device(apk_dest.c_str());
-            int status = stream_patch(apk_file[0], std::move(metadata.value()), std::move(patchFd));
-
-            result = pm_command(argc, argv);
-            delete_device_file(apk_dest);
-
-            return status;
-        }
+cleanup_apk:
+    if (use_fastdeploy == true) {
+#if defined(ENABLE_FASTDEPLOY)
+        delete_device_patch_file(apk_file[0]);
+#endif
     }
-
-    if (do_sync_push(apk_file, apk_dest.c_str(), false)) {
-        result = pm_command(argc, argv);
-        delete_device_file(apk_dest);
-    }
-
+    delete_device_file(apk_dest);
     return result;
 }
 
 int install_app(int argc, const char** argv) {
     std::vector<int> processedArgIndicies;
-    InstallMode installMode = INSTALL_DEFAULT;
+    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++) {
@@ -316,24 +328,34 @@
         } 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) {
-        installMode = best_install_mode();
+        if (use_legacy_install()) {
+            installMode = INSTALL_PUSH;
+        } else {
+            installMode = INSTALL_STREAM;
+        }
     }
 
-    if (installMode == INSTALL_STREAM && best_install_mode() == INSTALL_PUSH) {
+    if (installMode == INSTALL_STREAM && use_legacy_install() == true) {
         error_exit("Attempting to use streaming install on unsupported device");
     }
 
-    if (use_fastdeploy && get_device_api_level() < kFastDeployMinApi) {
+#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;
     }
-    fastdeploy_set_agent_update_strategy(agent_update_strategy);
+#endif
 
     std::vector<const char*> passthrough_argv;
     for (int i = 0; i < argc; i++) {
@@ -346,13 +368,26 @@
         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_fastdeploy, use_localagent);
         case INSTALL_STREAM:
             return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
-                                        use_fastdeploy);
+                                        use_fastdeploy, use_localagent);
         case INSTALL_DEFAULT:
         default:
             return 1;
@@ -374,8 +409,7 @@
             android::base::EndsWithIgnoreCase(file, ".dm") ||
             android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
             struct stat sb;
-            if (stat(file, &sb) == -1) perror_exit("failed to stat \"%s\"", file);
-            total_size += sb.st_size;
+            if (stat(file, &sb) != -1) total_size += sb.st_size;
             first_apk = i;
         } else {
             break;
@@ -385,7 +419,7 @@
     if (first_apk == -1) error_exit("need APK file on command line");
 
     std::string install_cmd;
-    if (best_install_mode() == INSTALL_PUSH) {
+    if (use_legacy_install()) {
         install_cmd = "exec:pm";
     } else {
         install_cmd = "exec:cmd package";
@@ -425,13 +459,13 @@
     }
 
     // Valid session, now stream the APKs
-    bool success = true;
+    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 = false;
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            success = 0;
             goto finalize_session;
         }
 
@@ -442,8 +476,8 @@
 
         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 = false;
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            success = 0;
             goto finalize_session;
         }
 
@@ -451,7 +485,7 @@
         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 = false;
+            success = 0;
             goto finalize_session;
         }
 
@@ -459,15 +493,15 @@
         read_status_line(remote_fd.get(), buf, sizeof(buf));
 
         if (strncmp("Success", buf, 7)) {
-            fprintf(stderr, "adb: failed to write \"%s\"\n", file);
+            fprintf(stderr, "adb: failed to write %s\n", file);
             fputs(buf, stderr);
-            success = false;
+            success = 0;
             goto finalize_session;
         }
     }
 
 finalize_session:
-    // Commit session if we streamed everything okay; otherwise abandon.
+    // 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);
     {
@@ -478,16 +512,14 @@
         }
         read_status_line(fd.get(), buf, sizeof(buf));
     }
-    if (!success) return EXIT_FAILURE;
 
-    if (strncmp("Success", buf, 7)) {
-        fprintf(stderr, "adb: failed to finalize session\n");
-        fputs(buf, stderr);
-        return EXIT_FAILURE;
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        return 0;
     }
-
-    fputs(buf, stdout);
-    return EXIT_SUCCESS;
+    fprintf(stderr, "adb: failed to finalize session\n");
+    fputs(buf, stderr);
+    return EXIT_FAILURE;
 }
 
 int install_multi_package(int argc, const char** argv) {
@@ -510,7 +542,7 @@
 
     if (first_package == -1) error_exit("need APK or APEX files on command line");
 
-    if (best_install_mode() == INSTALL_PUSH) {
+    if (use_legacy_install()) {
         fprintf(stderr, "adb: multi-package install is not supported on this device\n");
         return EXIT_FAILURE;
     }
@@ -707,20 +739,6 @@
 }
 
 int delete_device_file(const std::string& filename) {
-    // http://b/17339227 "Sideloading a Readonly File Results in a Prompt to
-    // Delete" caused us to add `-f` here, to avoid the equivalent of the `-i`
-    // prompt that you get from BSD rm (used in Android 5) if you have a
-    // non-writable file and stdin is a tty (which is true for old versions of
-    // adbd).
-    //
-    // Unfortunately, `rm -f` requires Android 4.3, so that workaround broke
-    // earlier Android releases. This was reported as http://b/37704384 "adb
-    // install -r passes invalid argument to rm on Android 4.1" and
-    // http://b/37035817 "ADB Fails: rm failed for -f, No such file or
-    // directory".
-    //
-    // Testing on a variety of devices and emulators shows that redirecting
-    // stdin is sufficient to avoid the pseudo-`-i`, and works on toolbox,
-    // BSD, and toybox versions of rm.
-    return send_shell_command("rm " + escape_arg(filename) + " </dev/null");
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return send_shell_command(cmd);
 }
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index e8be784..3eee426 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -53,25 +53,6 @@
     *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))) {
@@ -89,7 +70,6 @@
     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;
 }
 
@@ -99,7 +79,6 @@
     mode_t old_mask;
     FILE *f = nullptr;
     int ret = 0;
-    std::string pubkey;
 
     EVP_PKEY* pkey = EVP_PKEY_new();
     BIGNUM* exponent = BN_new();
@@ -113,11 +92,6 @@
     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");
@@ -130,12 +104,7 @@
     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";
+        D("Failed to write key");
         goto out;
     }
 
@@ -173,8 +142,7 @@
 
     RSA* key = RSA_new();
     if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
-        LOG(ERROR) << "Failed to read key from '" << file << "'";
-        ERR_print_errors_fp(stderr);
+        LOG(ERROR) << "Failed to read key";
         RSA_free(key);
         return nullptr;
     }
@@ -250,7 +218,7 @@
     return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
 }
 
-static bool load_userkey() {
+static bool generate_userkey() {
     std::string path = get_user_key_path();
     if (path.empty()) {
         PLOG(ERROR) << "Error getting user key filename";
@@ -436,8 +404,8 @@
 void adb_auth_init() {
     LOG(INFO) << "adb_auth_init...";
 
-    if (!load_userkey()) {
-        LOG(ERROR) << "Failed to load (or generate) user key";
+    if (!generate_userkey()) {
+        LOG(ERROR) << "Failed to generate user key";
         return;
     }
 
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 0ffdbc2..8dd85d7 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -96,8 +96,9 @@
         " version                  show version num\n"
         "\n"
         "networking:\n"
-        " connect HOST[:PORT]      connect to a device via TCP/IP\n"
-        " disconnect [[HOST]:PORT] disconnect from given TCP/IP device, or all\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"
@@ -127,9 +128,9 @@
         " 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 [all|data|odm|oem|product_services|product|system|vendor]\n"
         "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
-        "     -l: list files that would be copied, but don't copy them\n"
+        "     -l: list but don't copy\n"
         "\n"
         "shell:\n"
         " shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
@@ -153,7 +154,6 @@
         "     -d: allow version code downgrade (debuggable packages only)\n"
         "     -p: partial application install (install-multiple only)\n"
         "     -g: grant all runtime permissions\n"
-        "     --abi ABI: override platform's default ABI\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"
@@ -165,12 +165,14 @@
 #ifndef _WIN32
         "     --local-agent: locate agent files from local source build (instead of SDK location)\n"
 #endif
-        "     (See also `adb shell pm help` for more options.)\n"
         //TODO--installlog <filename>
         " 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"
@@ -255,8 +257,13 @@
 }
 #endif
 
-int read_and_dump(borrowed_fd fd, bool use_shell_protocol,
-                  StandardStreamsCallbackInterface* callback) {
+// 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;
 
@@ -298,9 +305,9 @@
             }
             length = protocol->data_length();
         } else {
-            D("read_and_dump(): pre adb_read(fd=%d)", fd.get());
+            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.get(), length);
+            D("read_and_dump(): post adb_read(fd=%d): length=%d", fd, length);
             if (length <= 0) {
                 break;
             }
@@ -352,7 +359,7 @@
 }
 
 void copy_to_file(int inFd, int outFd) {
-    std::vector<char> buf(64 * 1024);
+    std::vector<char> buf(32 * 1024);
     int len;
     long total = 0;
     int old_stdin_mode = -1;
@@ -771,16 +778,17 @@
         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);
+    std::string service_string("abb:");
+    for (auto i = optind; i < argc; ++i) {
+        service_string.append(argv[i]);
+        service_string.push_back(ABB_ARG_DELIMETER);
+    }
 
     D("abb -e 0x%x [%*.s]\n", escape_char, static_cast<int>(service_string.size()),
       service_string.data());
@@ -879,7 +887,7 @@
             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));
+        return adb_sideload_legacy(filename, package_fd, static_cast<int>(sb.st_size));
     }
 
     int opt = SIDELOAD_HOST_BLOCK_SIZE;
@@ -1134,7 +1142,7 @@
     // 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);
+        wait_for_device("wait-for-device", 3000ms);
     }
 
     return true;
@@ -1199,14 +1207,14 @@
     return send_shell_command(cmd);
 }
 
-static void write_zeros(int bytes, borrowed_fd fd) {
+static void write_zeros(int bytes, int fd) {
     int old_stdin_mode = -1;
     int old_stdout_mode = -1;
     std::vector<char> buf(bytes);
 
-    D("write_zeros(%d) -> %d", bytes, fd.get());
+    D("write_zeros(%d) -> %d", bytes, fd);
 
-    stdinout_raw_prologue(-1, fd.get(), old_stdin_mode, old_stdout_mode);
+    stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
 
     if (fd == STDOUT_FILENO) {
         fwrite(buf.data(), 1, bytes, stdout);
@@ -1215,14 +1223,12 @@
         adb_write(fd, buf.data(), bytes);
     }
 
-    stdinout_raw_prologue(-1, fd.get(), old_stdin_mode, old_stdout_mode);
+    stdinout_raw_prologue(-1, fd, 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 */
@@ -1272,8 +1278,6 @@
 }
 
 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];
@@ -1611,13 +1615,13 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) error_exit("usage: adb connect HOST[:PORT>]");
+        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]]");
+        if (argc > 2) error_exit("usage: adb disconnect [<host>[:<port>]]");
 
         std::string query = android::base::StringPrintf("host:disconnect:%s",
                                                         (argc == 2) ? argv[1] : "");
@@ -1699,27 +1703,10 @@
             error_exit("tcpip: invalid port: %s", argv[1]);
         }
         return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
-    } else if (!strcmp(argv[0], "remount")) {
-        FeatureSet features;
-        std::string error;
-        if (!adb_get_feature_set(&features, &error)) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
-        }
-
-        if (CanUseFeature(features, kFeatureRemountShell)) {
-            std::vector<const char*> args = {"shell"};
-            args.insert(args.cend(), argv, argv + argc);
-            return adb_shell(args.size(), args.data());
-        } else if (argc > 1) {
-            auto command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
-            return adb_connect_command(command);
-        } else {
-            return adb_connect_command("remount:");
-        }
     }
     // clang-format off
-    else if (!strcmp(argv[0], "reboot") ||
+    else if (!strcmp(argv[0], "remount") ||
+             !strcmp(argv[0], "reboot") ||
              !strcmp(argv[0], "reboot-bootloader") ||
              !strcmp(argv[0], "reboot-fastboot") ||
              !strcmp(argv[0], "usb") ||
@@ -1751,33 +1738,41 @@
         // Determine the <host-prefix> for this command.
         std::string host_prefix;
         if (reverse) {
-            host_prefix = "reverse:";
+            host_prefix = "reverse";
         } else {
-            host_prefix = "host:";
+            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");
+            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 = "killforward-all";
+            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 = std::string("killforward:") + argv[1];
+            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 = std::string("forward:norebind:") + argv[1] + ";" + argv[2];
+                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 = std::string("forward:") + argv[0] + ";" + argv[1];
+                cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
             }
         }
 
@@ -1785,7 +1780,7 @@
             error_exit("error: %s", error_message.c_str());
         }
 
-        unique_fd fd(adb_connect(nullptr, host_prefix + cmd, &error_message, true));
+        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());
         }
@@ -1827,7 +1822,7 @@
         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");
+        if (argc < 3) 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");
@@ -1847,8 +1842,8 @@
         }
 
         if (src.empty()) src = "all";
-        std::vector<std::string> partitions{"data",   "odm",        "oem",   "product",
-                                            "system", "system_ext", "vendor"};
+        std::vector<std::string> partitions{"data",   "odm",   "oem", "product", "product_services",
+                                            "system", "vendor"};
         bool found = false;
         for (const auto& partition : partitions) {
             if (src == "all" || src == partition) {
@@ -1896,10 +1891,7 @@
     } else if (!strcmp(argv[0], "track-jdwp")) {
         return adb_connect_command("track-jdwp");
     } else if (!strcmp(argv[0], "track-devices")) {
-        if (argc > 2 || (argc == 2 && strcmp(argv[1], "-l"))) {
-            error_exit("usage: adb track-devices [-l]");
-        }
-        return adb_connect_command(argc == 2 ? "host:track-devices-l" : "host:track-devices");
+        return adb_connect_command("host:track-devices");
     } else if (!strcmp(argv[0], "raw")) {
         if (argc != 2) {
             error_exit("usage: adb raw SERVICE");
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index ab77b29..6cfd4f7 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -17,11 +17,7 @@
 #ifndef COMMANDLINE_H
 #define COMMANDLINE_H
 
-#include <android-base/strings.h>
-
 #include "adb.h"
-#include "adb_client.h"
-#include "adb_unique_fd.h"
 
 // Callback used to handle the standard streams (stdout and stderr) sent by the
 // device's upon receiving a command.
@@ -109,25 +105,4 @@
         const std::string& command, bool disable_shell_protocol = false,
         StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
 
-// 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);
-
-// Connects to the device "abb" service with |command| and returns the fd.
-template <typename ContainerT>
-unique_fd send_abb_exec_command(const ContainerT& command_args, std::string* error) {
-    std::string service_string = "abb_exec:" + android::base::Join(command_args, ABB_ARG_DELIMETER);
-
-    unique_fd fd(adb_connect(service_string, error));
-    if (fd < 0) {
-        fprintf(stderr, "adb: failed to run abb_exec. Error: %s\n", error->c_str());
-        return unique_fd{};
-    }
-    return fd;
-}
-
 #endif  // COMMANDLINE_H
diff --git a/adb/client/console.cpp b/adb/client/console.cpp
index d10f4de..1dbb6e2 100644
--- a/adb/client/console.cpp
+++ b/adb/client/console.cpp
@@ -71,7 +71,7 @@
         return -1;
     }
 
-    int port = -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) {
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index 5fa0edb..f4e8664 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -27,112 +27,124 @@
 #include "androidfw/ZipFileRO.h"
 #include "client/file_sync_client.h"
 #include "commandline.h"
-#include "deployagent.inc"        // Generated include via build rule.
-#include "deployagentscript.inc"  // Generated include via build rule.
-#include "fastdeploy/deploypatchgenerator/deploy_patch_generator.h"
-#include "fastdeploy/deploypatchgenerator/patch_utils.h"
-#include "fastdeploy/proto/ApkEntry.pb.h"
 #include "fastdeploycallbacks.h"
 #include "sysdeps.h"
 
 #include "adb_utils.h"
 
-static constexpr long kRequiredAgentVersion = 0x00000003;
+static constexpr long kRequiredAgentVersion = 0x00000002;
 
-static constexpr int kPackageMissing = 3;
-static constexpr int kInvalidAgentVersion = 4;
+static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
 
-static constexpr const char* kDeviceAgentFile = "/data/local/tmp/deployagent.jar";
-static constexpr const char* kDeviceAgentScript = "/data/local/tmp/deployagent";
+static bool g_use_localagent = false;
 
-static constexpr bool g_verbose_timings = false;
-static FastDeploy_AgentUpdateStrategy g_agent_update_strategy =
-        FastDeploy_AgentUpdateDifferentVersion;
+long get_agent_version() {
+    std::vector<char> versionOutputBuffer;
+    std::vector<char> versionErrorBuffer;
 
-using APKMetaData = com::android::fastdeploy::APKMetaData;
+    int statusCode = capture_shell_command("/data/local/tmp/deployagent version",
+                                           &versionOutputBuffer, &versionErrorBuffer);
+    long version = -1;
 
-namespace {
-
-struct TimeReporter {
-    TimeReporter(const char* label) : label_(label) {}
-    ~TimeReporter() {
-        if (g_verbose_timings) {
-            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
-                    std::chrono::steady_clock::now() - start_);
-            fprintf(stderr, "%s finished in %lldms\n", label_,
-                    static_cast<long long>(duration.count()));
-        }
+    if (statusCode == 0 && versionOutputBuffer.size() > 0) {
+        version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
     }
 
-  private:
-    const char* label_;
-    std::chrono::steady_clock::time_point start_ = std::chrono::steady_clock::now();
-};
-#define REPORT_FUNC_TIME() TimeReporter reporter(__func__)
-
-struct FileDeleter {
-    FileDeleter(const char* path) : path_(path) {}
-    ~FileDeleter() { adb_unlink(path_); }
-
-  private:
-    const char* const path_;
-};
-
-}  // namespace
+    return version;
+}
 
 int get_device_api_level() {
-    REPORT_FUNC_TIME();
-    std::vector<char> sdk_version_output_buffer;
-    std::vector<char> sdk_version_error_buffer;
+    std::vector<char> sdkVersionOutputBuffer;
+    std::vector<char> sdkVersionErrorBuffer;
     int api_level = -1;
 
-    int statusCode = capture_shell_command("getprop ro.build.version.sdk",
-                                           &sdk_version_output_buffer, &sdk_version_error_buffer);
-    if (statusCode == 0 && sdk_version_output_buffer.size() > 0) {
-        api_level = strtol((char*)sdk_version_output_buffer.data(), NULL, 10);
+    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_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy) {
-    g_agent_update_strategy = agent_update_strategy;
+void fastdeploy_set_local_agent(bool use_localagent) {
+    g_use_localagent = use_localagent;
 }
 
-static void push_to_device(const void* data, size_t byte_count, const char* dst, bool sync) {
-    std::vector<const char*> srcs;
-    TemporaryFile tf;
-    android::base::WriteFully(tf.fd, data, byte_count);
-    srcs.push_back(tf.path);
-    // On Windows, the file needs to be flushed before pushing to device,
-    // but can't be removed until after the push.
-    unix_close(tf.release());
+// 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 (!do_sync_push(srcs, dst, sync)) {
-        error_exit("Failed to push fastdeploy agent to device.");
+    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 check_time_stamps) {
-    REPORT_FUNC_TIME();
+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());
 
-    push_to_device(kDeployAgent, sizeof(kDeployAgent), kDeviceAgentFile, check_time_stamps);
-    push_to_device(kDeployAgentScript, sizeof(kDeployAgentScript), kDeviceAgentScript,
-                   check_time_stamps);
-
-    // on windows the shell script might have lost execute permission
-    // so need to set this explicitly
-    const char* kChmodCommandPattern = "chmod 777 %s";
-    std::string chmod_command =
-            android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentScript);
-    int ret = send_shell_command(chmod_command);
-    if (ret != 0) {
-        error_exit("Error executing %s returncode: %d", chmod_command.c_str(), ret);
+    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) {
@@ -144,29 +156,29 @@
     return utf8;
 }
 
-static std::string get_package_name_from_apk(const char* apk_path) {
+static std::string get_packagename_from_apk(const char* apkPath) {
 #undef open
-    std::unique_ptr<android::ZipFileRO> zip_file((android::ZipFileRO::open)(apk_path));
+    std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
 #define open ___xxx_unix_open
-    if (zip_file == nullptr) {
-        perror_exit("Could not open %s", apk_path);
+    if (zipFile == nullptr) {
+        perror_exit("Could not open %s", apkPath);
     }
-    android::ZipEntryRO entry = zip_file->findEntryByName("AndroidManifest.xml");
+    android::ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
     if (entry == nullptr) {
-        error_exit("Could not find AndroidManifest.xml inside %s", apk_path);
+        error_exit("Could not find AndroidManifest.xml inside %s", apkPath);
     }
     uint32_t manifest_len = 0;
-    if (!zip_file->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
-        error_exit("Could not read AndroidManifest.xml inside %s", apk_path);
+    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 (!zip_file->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
-        error_exit("Could not uncompress AndroidManifest.xml inside %s", apk_path);
+    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", apk_path);
+        error_exit("Could not parse AndroidManifest.xml inside %s", apkPath);
     }
     android::ResXMLParser::event_code_t code;
     while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
@@ -207,97 +219,99 @@
                 break;
         }
     }
-    error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apk_path);
+    error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
 }
 
-static long parse_agent_version(const std::vector<char>& version_buffer) {
-    long version = -1;
-    if (!version_buffer.empty()) {
-        version = strtol((char*)version_buffer.data(), NULL, 16);
-    }
-    return version;
-}
+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());
 
-static void update_agent_if_necessary() {
-    switch (g_agent_update_strategy) {
-        case FastDeploy_AgentUpdateAlways:
-            deploy_agent(/*check_time_stamps=*/false);
-            break;
-        case FastDeploy_AgentUpdateNewerTimeStamp:
-            deploy_agent(/*check_time_stamps=*/true);
-            break;
-        default:
-            break;
-    }
-}
-
-std::optional<APKMetaData> extract_metadata(const char* apk_path) {
-    // Update agent if there is a command line argument forcing to do so.
-    update_agent_if_necessary();
-
-    REPORT_FUNC_TIME();
-
-    std::string package_name = get_package_name_from_apk(apk_path);
-
-    // Dump apk command checks the required vs current agent version and if they match then returns
-    // the APK dump for package. Doing this in a single call saves round-trip and agent launch time.
-    constexpr const char* kAgentDumpCommandPattern = "/data/local/tmp/deployagent dump %ld %s";
-    std::string dump_command = android::base::StringPrintf(
-            kAgentDumpCommandPattern, kRequiredAgentVersion, package_name.c_str());
-
-    std::vector<char> dump_out_buffer;
-    std::vector<char> dump_error_buffer;
-    int returnCode =
-            capture_shell_command(dump_command.c_str(), &dump_out_buffer, &dump_error_buffer);
-    if (returnCode >= kInvalidAgentVersion) {
-        // Agent has wrong version or missing.
-        long agent_version = parse_agent_version(dump_out_buffer);
-        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(/*check_time_stamps=*/false);
-
-        // Retry with new agent.
-        dump_out_buffer.clear();
-        dump_error_buffer.clear();
-        returnCode =
-                capture_shell_command(dump_command.c_str(), &dump_out_buffer, &dump_error_buffer);
-    }
+    std::vector<char> extractErrorBuffer;
+    DeployAgentFileCallback cb(outputFp, &extractErrorBuffer);
+    int returnCode = send_shell_command(extractCommand, false, &cb);
     if (returnCode != 0) {
-        if (returnCode == kInvalidAgentVersion) {
-            long agent_version = parse_agent_version(dump_out_buffer);
-            error_exit(
-                    "After update agent version remains incorrect! Expected %ld but version is %ld",
-                    kRequiredAgentVersion, agent_version);
-        }
-        if (returnCode == kPackageMissing) {
-            fprintf(stderr, "Package %s not found, falling back to install\n",
-                    package_name.c_str());
-            return {};
-        }
-        fprintf(stderr, "Executing %s returned %d\n", dump_command.c_str(), returnCode);
-        fprintf(stderr, "%*s\n", int(dump_error_buffer.size()), dump_error_buffer.data());
+        fprintf(stderr, "Executing %s returned %d\n", extractCommand.c_str(), returnCode);
+        fprintf(stderr, "%*s\n", int(extractErrorBuffer.size()), extractErrorBuffer.data());
         error_exit("Aborting");
     }
-
-    com::android::fastdeploy::APKDump dump;
-    if (!dump.ParseFromArray(dump_out_buffer.data(), dump_out_buffer.size())) {
-        fprintf(stderr, "Can't parse output of %s\n", dump_command.c_str());
-        error_exit("Aborting");
-    }
-
-    return PatchUtils::GetDeviceAPKMetaData(dump);
 }
 
-unique_fd install_patch(int argc, const char** argv) {
-    REPORT_FUNC_TIME();
-    constexpr char kAgentApplyServicePattern[] = "shell:/data/local/tmp/deployagent apply - -pm %s";
+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::vector<unsigned char> apply_output_buffer;
-    std::vector<unsigned char> apply_error_buffer;
+    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;
@@ -312,42 +326,17 @@
         argsString.append("-r");
     }
 
-    std::string error;
-    std::string apply_patch_service_string =
-            android::base::StringPrintf(kAgentApplyServicePattern, argsString.c_str());
-    unique_fd fd{adb_connect(apply_patch_service_string, &error)};
-    if (fd < 0) {
-        error_exit("Executing %s returned %s", apply_patch_service_string.c_str(), error.c_str());
-    }
-    return fd;
-}
-
-unique_fd apply_patch_on_device(const char* output_path) {
-    REPORT_FUNC_TIME();
-    constexpr char kAgentApplyServicePattern[] = "shell:/data/local/tmp/deployagent apply - -o %s";
-
-    std::string error;
-    std::string apply_patch_service_string =
-            android::base::StringPrintf(kAgentApplyServicePattern, output_path);
-    unique_fd fd{adb_connect(apply_patch_service_string, &error)};
-    if (fd < 0) {
-        error_exit("Executing %s returned %s", apply_patch_service_string.c_str(), error.c_str());
-    }
-    return fd;
-}
-
-static void create_patch(const char* apk_path, APKMetaData metadata, borrowed_fd patch_fd) {
-    REPORT_FUNC_TIME();
-    DeployPatchGenerator generator(/*is_verbose=*/false);
-    bool success = generator.CreatePatch(apk_path, std::move(metadata), patch_fd);
-    if (!success) {
-        error_exit("Failed to create patch for %s", apk_path);
+    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);
     }
 }
 
-int stream_patch(const char* apk_path, APKMetaData metadata, unique_fd patch_fd) {
-    create_patch(apk_path, std::move(metadata), patch_fd);
-
-    REPORT_FUNC_TIME();
-    return read_and_dump(patch_fd.get());
+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
index 830aeb2..7b7f2ec 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -16,11 +16,6 @@
 
 #pragma once
 
-#include "adb_unique_fd.h"
-
-#include "fastdeploy/proto/ApkEntry.pb.h"
-
-#include <optional>
 #include <string>
 
 enum FastDeploy_AgentUpdateStrategy {
@@ -29,11 +24,12 @@
     FastDeploy_AgentUpdateDifferentVersion
 };
 
-void fastdeploy_set_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy);
+void fastdeploy_set_local_agent(bool use_localagent);
 int get_device_api_level();
-
-std::optional<com::android::fastdeploy::APKMetaData> extract_metadata(const char* apk_path);
-unique_fd install_patch(int argc, const char** argv);
-unique_fd apply_patch_on_device(const char* output_path);
-int stream_patch(const char* apk_path, com::android::fastdeploy::APKMetaData metadata,
-                 unique_fd patch_fd);
+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
index 86ceaa1..23a0aca 100644
--- a/adb/client/fastdeploycallbacks.cpp
+++ b/adb/client/fastdeploycallbacks.cpp
@@ -49,7 +49,35 @@
 int capture_shell_command(const char* command, std::vector<char>* outBuffer,
                           std::vector<char>* errBuffer) {
     DeployAgentBufferCallback cb(outBuffer, errBuffer);
-    return send_shell_command(command, /*disable_shell_protocol=*/false, &cb);
+    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,
diff --git a/adb/client/fastdeploycallbacks.h b/adb/client/fastdeploycallbacks.h
index 4a6fb99..7e049c5 100644
--- a/adb/client/fastdeploycallbacks.h
+++ b/adb/client/fastdeploycallbacks.h
@@ -17,6 +17,23 @@
 #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
index 703eb2f..5d10238 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -849,16 +849,6 @@
     return true;
 }
 
-// dirname("//foo") returns "//", so we can't do the obvious `path == "/"`.
-static bool is_root_dir(std::string_view path) {
-    for (char c : path) {
-        if (c != '/') {
-            return false;
-        }
-    }
-    return true;
-}
-
 static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
                                   std::string rpath, bool check_timestamps,
                                   bool list_only) {
@@ -872,8 +862,8 @@
     std::vector<copyinfo> file_list;
     std::vector<std::string> directory_list;
 
-    for (std::string path = rpath; !is_root_dir(path); path = android::base::Dirname(path)) {
-        directory_list.push_back(path);
+    for (std::string dirpath = rpath; dirpath != "/"; dirpath = android::base::Dirname(dirpath)) {
+        directory_list.push_back(dirpath);
     }
     std::reverse(directory_list.begin(), directory_list.end());
 
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 1a34384..283fac5 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -31,7 +31,7 @@
 
 #include "adb_mdns.h"
 #include "adb_trace.h"
-#include "fdevent/fdevent.h"
+#include "fdevent.h"
 #include "sysdeps.h"
 
 static DNSServiceRef service_ref;
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 343e7b5..81b8306 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -324,7 +324,7 @@
 
     h->urb_out_busy = true;
     while (true) {
-        auto now = std::chrono::steady_clock::now();
+        auto now = std::chrono::system_clock::now();
         if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
             // TODO: call USBDEVFS_DISCARDURB?
             errno = ETIMEDOUT;
@@ -406,44 +406,25 @@
     }
 }
 
-static int usb_write_split(usb_handle* h, unsigned char* data, int len) {
-    for (int i = 0; i < len; i += 16384) {
-        int chunk_size = (i + 16384 > len) ? len - i : 16384;
-        int n = usb_bulk_write(h, data + i, chunk_size);
-        if (n != chunk_size) {
-            D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
-            return -1;
-        }
-    }
-
-    return len;
-}
-
-int usb_write(usb_handle* h, const void* _data, int len) {
+int usb_write(usb_handle *h, const void *_data, int len)
+{
     D("++ usb_write ++");
 
-    unsigned char* data = (unsigned char*)_data;
-
-    // The kernel will attempt to allocate a contiguous buffer for each write we submit.
-    // This might fail due to heap fragmentation, so attempt a contiguous write once, and if that
-    // fails, retry after having split the data into 16kB chunks to avoid allocation failure.
+    unsigned char *data = (unsigned char*) _data;
     int n = usb_bulk_write(h, data, len);
-    if (n == -1 && errno == ENOMEM) {
-        n = usb_write_split(h, data, len);
-    }
-
-    if (n == -1) {
+    if (n != len) {
+        D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
         return -1;
     }
 
     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) == 0 ? len : -1;
+        return usb_bulk_write(h, _data, 0) == 0 ? n : -1;
     }
 
     D("-- usb_write --");
-    return len;
+    return n;
 }
 
 int usb_read(usb_handle *h, void *_data, int len)
@@ -609,7 +590,6 @@
     while (true) {
         // TODO: Use inotify.
         find_usb_device("/dev/bus/usb", register_device);
-        adb_notify_device_scan_complete();
         kick_disconnected_devices();
         std::this_thread::sleep_for(1s);
     }
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 7207ca7..5c0da47 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -461,7 +461,6 @@
             std::this_thread::sleep_for(100ms);
         }
 
-        adb_notify_device_scan_complete();
         initialized = true;
     }
 }
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 197c6fa..f23c3a5 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -172,7 +172,6 @@
 
     while (true) {
         find_devices();
-        adb_notify_device_scan_complete();
         std::this_thread::sleep_for(1s);
     }
 }
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index 17c25e8..eeac41a 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -17,24 +17,22 @@
 #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"
-#include "sysdeps.h"
 
 namespace {
 
 class AdbFdTextOutput : public android::TextOutput {
   public:
-    explicit AdbFdTextOutput(borrowed_fd fd) : fd_(fd) {}
+    explicit AdbFdTextOutput(int fd) : mFD(fd) {}
 
   private:
     android::status_t print(const char* txt, size_t len) override {
-        return WriteFdExactly(fd_, txt, len) ? android::OK : -errno;
+        return WriteFdExactly(mFD, txt, len) ? android::OK : -errno;
     }
     void moveIndent(int delta) override { /*not implemented*/
     }
@@ -45,7 +43,7 @@
     }
 
   private:
-    borrowed_fd fd_;
+    int mFD;
 };
 
 std::vector<std::string_view> parseCmdArgs(std::string_view args) {
@@ -69,16 +67,10 @@
 
 }  // namespace
 
-static int execCmd(std::string_view args, borrowed_fd in, borrowed_fd out, borrowed_fd err) {
-    int max_buf = LINUX_MAX_SOCKET_SIZE;
-    adb_setsockopt(in, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
-    adb_setsockopt(out, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
-    adb_setsockopt(err, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
-
+static int execCmd(std::string_view args, int in, int out, int err) {
     AdbFdTextOutput oin(out);
     AdbFdTextOutput oerr(err);
-    return cmdMain(parseCmdArgs(args), oin, oerr, in.get(), out.get(), err.get(),
-                   RunMode::kLibrary);
+    return cmdMain(parseCmdArgs(args), oin, oerr, in, out, err, RunMode::kLibrary);
 }
 
 int main(int argc, char* const argv[]) {
@@ -95,17 +87,15 @@
 
         std::string_view name = data;
         auto protocol = SubprocessProtocol::kShell;
-        if (android::base::ConsumePrefix(&name, "abb:")) {
+        if (ConsumePrefix(&name, "abb:")) {
             protocol = SubprocessProtocol::kShell;
-        } else if (android::base::ConsumePrefix(&name, "abb_exec:")) {
+        } else if (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);
-        int max_buf = LINUX_MAX_SOCKET_SIZE;
-        adb_setsockopt(result, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
         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
index e1df4a5..a435279 100644
--- a/adb/daemon/abb_service.cpp
+++ b/adb/daemon/abb_service.cpp
@@ -53,13 +53,14 @@
             return error_fd;
         }
 
-        if (!SendProtocolString(socket_fd_, command)) {
+        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";
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 2e84ce6..a18afa4 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -19,7 +19,7 @@
 #include "adb.h"
 #include "adb_auth.h"
 #include "adb_io.h"
-#include "fdevent/fdevent.h"
+#include "fdevent.h"
 #include "sysdeps.h"
 #include "transport.h"
 
@@ -31,7 +31,6 @@
 #include <algorithm>
 #include <memory>
 
-#include <adbd_auth.h>
 #include <android-base/file.h>
 #include <android-base/strings.h>
 #include <crypto_utils/android_pubkey.h>
@@ -39,57 +38,85 @@
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
 
-static AdbdAuthContext* auth_ctx;
+static fdevent* listener_fde = nullptr;
+static fdevent* framework_fde = nullptr;
+static auto& framework_mutex = *new std::mutex();
+static int framework_fd GUARDED_BY(framework_mutex) = -1;
+static auto& connected_keys GUARDED_BY(framework_mutex) = *new std::vector<std::string>;
 
 static void adb_disconnected(void* unused, atransport* t);
 static struct adisconnect adb_disconnect = {adb_disconnected, nullptr};
+static atransport* adb_transport;
+static bool needs_retry = false;
 
 bool auth_required = true;
 
-static void IteratePublicKeys(std::function<bool(std::string_view public_key)> f) {
-    adbd_auth_get_public_keys(
-            auth_ctx,
-            [](const char* public_key, size_t len, void* arg) {
-                return (*static_cast<decltype(f)*>(arg))(std::string_view(public_key, len));
-            },
-            &f);
-}
-
 bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig,
                       std::string* auth_key) {
-    bool authorized = false;
+    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")) {
+                if (line.empty()) continue;
+                *auth_key = line;
+                // 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;
+            }
+        }
+    }
     auth_key->clear();
+    return false;
+}
 
-    IteratePublicKeys([&](std::string_view public_key) {
-        // TODO: do we really have to support both ' ' and '\t'?
-        std::vector<std::string> split = android::base::Split(std::string(public_key), " \t");
-        uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
-        const std::string& pubkey = split[0];
-        if (b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)) != ANDROID_PUBKEY_ENCODED_SIZE) {
-            LOG(ERROR) << "Invalid base64 key " << pubkey;
-            return true;
-        }
+static bool adbd_send_key_message_locked(std::string_view msg_type, std::string_view key)
+        REQUIRES(framework_mutex) {
+    if (framework_fd < 0) {
+        LOG(ERROR) << "Client not connected to send msg_type " << msg_type;
+        return false;
+    }
+    std::string msg = std::string(msg_type) + std::string(key);
+    int msg_len = msg.length();
+    if (msg_len >= static_cast<int>(MAX_FRAMEWORK_PAYLOAD)) {
+        LOG(ERROR) << "Key too long (" << msg_len << ")";
+        return false;
+    }
 
-        RSA* key = nullptr;
-        if (!android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
-            LOG(ERROR) << "Failed to parse key " << pubkey;
-            return true;
-        }
-
-        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) {
-            *auth_key = public_key;
-            authorized = true;
-            return false;
-        }
-
-        return true;
-    });
-
-    return authorized;
+    LOG(DEBUG) << "Sending '" << msg << "'";
+    if (!WriteFdExactly(framework_fd, msg.c_str(), msg_len)) {
+        PLOG(ERROR) << "Failed to write " << msg_type;
+        return false;
+    }
+    return true;
 }
 
 static bool adbd_auth_generate_token(void* token, size_t token_size) {
@@ -100,6 +127,113 @@
     return okay;
 }
 
+static void adb_disconnected(void* unused, atransport* t) {
+    LOG(INFO) << "ADB disconnect";
+    adb_transport = nullptr;
+    needs_retry = false;
+    {
+        std::lock_guard<std::mutex> lock(framework_mutex);
+        if (framework_fd >= 0) {
+            adbd_send_key_message_locked("DC", t->auth_key);
+        }
+        connected_keys.erase(std::remove(connected_keys.begin(), connected_keys.end(), t->auth_key),
+                             connected_keys.end());
+    }
+}
+
+static void framework_disconnected() {
+    LOG(INFO) << "Framework disconnect";
+    if (framework_fde) {
+        fdevent_destroy(framework_fde);
+        {
+            std::lock_guard<std::mutex> lock(framework_mutex);
+            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 (adb_transport) {
+                adbd_auth_verified(adb_transport);
+            }
+        }
+    }
+}
+
+void adbd_auth_confirm_key(atransport* t) {
+    if (!adb_transport) {
+        adb_transport = t;
+        t->AddDisconnect(&adb_disconnect);
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(framework_mutex);
+        if (framework_fd < 0) {
+            LOG(ERROR) << "Client not connected";
+            needs_retry = true;
+            return;
+        }
+
+        adbd_send_key_message_locked("PK", t->auth_key);
+    }
+}
+
+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;
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(framework_mutex);
+        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(adb_transport);
+        }
+
+        // if a client connected before the framework was available notify the framework of the
+        // connected key now.
+        if (!connected_keys.empty()) {
+            for (const auto& key : connected_keys) {
+                adbd_send_key_message_locked("CK", key);
+            }
+        }
+    }
+}
+
+void adbd_notify_framework_connected_key(atransport* t) {
+    if (!adb_transport) {
+        adb_transport = t;
+        t->AddDisconnect(&adb_disconnect);
+    }
+    {
+        std::lock_guard<std::mutex> lock(framework_mutex);
+        if (std::find(connected_keys.begin(), connected_keys.end(), t->auth_key) ==
+            connected_keys.end()) {
+            connected_keys.push_back(t->auth_key);
+        }
+        if (framework_fd >= 0) {
+            adbd_send_key_message_locked("CK", t->auth_key);
+        }
+    }
+}
+
 void adbd_cloexec_auth_socket() {
     int fd = android_get_control_socket("adbd");
     if (fd == -1) {
@@ -109,23 +243,20 @@
     fcntl(fd, F_SETFD, FD_CLOEXEC);
 }
 
-static void adbd_auth_key_authorized(void* arg, uint64_t id) {
-    LOG(INFO) << "adb client authorized";
-    auto* transport = static_cast<atransport*>(arg);
-    transport->auth_id = id;
-    adbd_auth_verified(transport);
-}
-
 void adbd_auth_init(void) {
-    AdbdAuthCallbacks cb;
-    cb.version = 1;
-    cb.callbacks.v1.key_authorized = adbd_auth_key_authorized;
-    auth_ctx = adbd_auth_new(&cb);
-    std::thread([]() {
-        adb_thread_setname("adbd auth");
-        adbd_auth_run(auth_ctx);
-        LOG(FATAL) << "auth thread terminated";
-    }).detach();
+    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) {
@@ -149,18 +280,3 @@
     handle_online(t);
     send_connect(t);
 }
-
-static void adb_disconnected(void* unused, atransport* t) {
-    LOG(INFO) << "ADB disconnect";
-    adbd_auth_notify_disconnect(auth_ctx, t->auth_id);
-}
-
-void adbd_auth_confirm_key(atransport* t) {
-    LOG(INFO) << "prompting user to authorize key";
-    t->AddDisconnect(&adb_disconnect);
-    adbd_auth_prompt_user(auth_ctx, t->auth_key.data(), t->auth_key.size(), t);
-}
-
-void adbd_notify_framework_connected_key(atransport* t) {
-    adbd_auth_notify_auth(auth_ctx, t->auth_key.data(), t->auth_key.size());
-}
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 0e70d47..e82a51f 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -229,13 +229,17 @@
 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 (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(Dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
@@ -266,12 +270,6 @@
         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;
 
@@ -466,9 +464,8 @@
         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));
+    if (posix_fadvise(fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE) < 0) {
+        D("[ Failed to fadvise: %d ]", errno);
     }
 
     syncmsg msg;
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 676f8e9..2a6418a 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -33,6 +33,7 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_utils.h"
+#include "fdevent.h"
 
 /* TODO:
 ** - sync with vsync to avoid tearing
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
index 2204246..fca3c58 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -16,8 +16,6 @@
  * limitations under the License.
  */
 
-#include <linux/usb/functionfs.h>
-
 #include <atomic>
 #include <condition_variable>
 #include <mutex>
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index b92a7de..66bfc0d 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -30,21 +30,15 @@
 
 #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
@@ -139,16 +133,16 @@
 static auto& _jdwp_list = *new std::list<std::unique_ptr<JdwpProcess>>();
 
 struct JdwpProcess {
-    JdwpProcess(unique_fd socket, pid_t pid) {
-        CHECK(pid != 0);
-
+    explicit JdwpProcess(int socket) {
         this->socket = socket;
-        this->pid = pid;
-        this->fde = fdevent_create(socket.release(), jdwp_process_event, this);
+        this->fde = fdevent_create(socket, jdwp_process_event, this);
 
         if (!this->fde) {
             LOG(FATAL) << "could not create fdevent for new JDWP process";
         }
+
+        /* start by waiting for the PID */
+        fdevent_add(this->fde, FDE_READ);
     }
 
     ~JdwpProcess() {
@@ -166,12 +160,18 @@
     }
 
     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);
     }
 
-    borrowed_fd socket = -1;
     int32_t pid = -1;
+    int socket = -1;
     fdevent* fde = nullptr;
 
     std::vector<unique_fd> out_fds;
@@ -181,6 +181,11 @@
     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);
@@ -209,12 +214,24 @@
 
 static void jdwp_process_event(int socket, unsigned events, void* _proc) {
     JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
-    CHECK_EQ(socket, proc->socket.get());
+    CHECK_EQ(socket, proc->socket);
 
     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 (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 {
+            // 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) {
@@ -267,6 +284,98 @@
     return unique_fd{};
 }
 
+/**  VM DEBUG CONTROL SOCKET
+ **
+ **  we do implement a custom asocket to receive the data
+ **/
+
+/* name of the debug control Unix socket */
+#define JDWP_CONTROL_NAME "\0jdwp-control"
+#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
+
+struct JdwpControl {
+    int listen_socket;
+    fdevent* fde;
+};
+
+static JdwpControl _jdwp_control;
+
+static void jdwp_control_event(int s, unsigned events, void* user);
+
+static int jdwp_control_init(JdwpControl* control, const char* sockname, int socknamelen) {
+    sockaddr_un addr;
+    socklen_t addrlen;
+    int 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);
+
+    unique_fd 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));
+        return -1;
+    }
+
+    if (listen(s, 4) < 0) {
+        D("listen failed in jdwp control socket: %d: %s", errno, strerror(errno));
+        return -1;
+    }
+
+    control->listen_socket = s.release();
+    control->fde = fdevent_create(control->listen_socket, jdwp_control_event, control);
+    if (control->fde == nullptr) {
+        D("could not create fdevent for jdwp control socket");
+        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 fd, unsigned events, void* _control) {
+    JdwpControl* control = (JdwpControl*)_control;
+
+    CHECK_EQ(fd, control->listen_socket);
+    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) {
+            LOG(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
  **/
@@ -417,22 +526,7 @@
 }
 
 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;
+    return jdwp_control_init(&_jdwp_control, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
 }
 
 #endif /* !ADB_HOST */
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 7277cc8..e5a4917 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -205,11 +205,7 @@
     // descriptor will always be open.
     adbd_cloexec_auth_socket();
 
-#if defined(__ANDROID_RECOVERY__)
-    if (is_device_unlocked() || __android_log_is_debuggable()) {
-        auth_required = false;
-    }
-#elif defined(ALLOW_ADBD_NO_AUTH)
+#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__)
@@ -218,6 +214,8 @@
     }
 #endif
 
+    adbd_auth_init();
+
     // Our external storage path may be different than apps, since
     // we aren't able to bind mount after dropping root.
     const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
@@ -232,9 +230,6 @@
     drop_privileges(server_port);
 #endif
 
-    // adbd_auth_init will spawn a thread, so we need to defer it until after selinux transitions.
-    adbd_auth_init();
-
     bool is_usb = false;
 
 #if defined(__ANDROID__)
@@ -253,12 +248,6 @@
         prop_port = android::base::GetProperty("persist.adb.tcp.port", "");
     }
 
-#if !defined(__ANDROID__)
-    if (prop_port.empty() && getenv("ADBD_PORT")) {
-        prop_port = getenv("ADBD_PORT");
-    }
-#endif
-
     int port;
     if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
         D("using port=%d", port);
diff --git a/adb/daemon/reboot_service.cpp b/adb/daemon/reboot_service.cpp
new file mode 100644
index 0000000..a5a11b8
--- /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, 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/libmodprobe/libmodprobe_test.h b/adb/daemon/reboot_service.h
similarity index 76%
copy from libmodprobe/libmodprobe_test.h
copy to adb/daemon/reboot_service.h
index a001b69..f68913e 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/adb/daemon/reboot_service.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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.
@@ -17,7 +17,9 @@
 #pragma once
 
 #include <string>
-#include <vector>
 
-extern std::vector<std::string> test_modules;
-extern std::vector<std::string> modules_loaded;
+#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/libmodprobe/libmodprobe_test.h b/adb/daemon/remount_service.h
similarity index 77%
rename from libmodprobe/libmodprobe_test.h
rename to adb/daemon/remount_service.h
index a001b69..522a5da 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/adb/daemon/remount_service.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -17,7 +17,9 @@
 #pragma once
 
 #include <string>
-#include <vector>
 
-extern std::vector<std::string> test_modules;
-extern std::vector<std::string> modules_loaded;
+#include "adb_unique_fd.h"
+
+#if defined(__ANDROID__)
+void remount_service(unique_fd, const std::string&);
+#endif
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index a44c10b..b0cc450 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -53,7 +53,10 @@
 
 #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"
 
 
@@ -220,13 +223,13 @@
         return create_jdwp_service_socket();
     } else if (name == "track-jdwp") {
         return create_jdwp_tracker_service_socket();
-    } else if (android::base::ConsumePrefix(&name, "sink:")) {
+    } else if (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:")) {
+    } else if (ConsumePrefix(&name, "source:")) {
         uint64_t byte_count = 0;
         if (!ParseUint(&byte_count, name)) {
             return nullptr;
@@ -247,19 +250,19 @@
 #if defined(__ANDROID__)
     if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
-    } else if (android::base::ConsumePrefix(&name, "remount:")) {
-        std::string cmd = "/system/bin/remount ";
-        cmd += name;
-        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if (android::base::ConsumePrefix(&name, "reboot:")) {
-        std::string cmd = "/system/bin/reboot ";
-        cmd += name;
-        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (ConsumePrefix(&name, "remount:")) {
+        std::string arg(name);
+        return create_service_thread("remount",
+                                     std::bind(remount_service, std::placeholders::_1, arg));
+    } else if (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:")) {
+    } else if (ConsumePrefix(&name, "backup:")) {
         std::string cmd = "/system/bin/bu backup ";
         cmd += name;
         return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
@@ -267,12 +270,12 @@
         return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
     } else if (name.starts_with("disable-verity:")) {
-        return StartSubprocess("/system/bin/disable-verity", nullptr, SubprocessType::kRaw,
-                               SubprocessProtocol::kNone);
+        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 StartSubprocess("/system/bin/enable-verity", nullptr, SubprocessType::kRaw,
-                               SubprocessProtocol::kNone);
-    } else if (android::base::ConsumePrefix(&name, "tcpip:")) {
+        return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
+                                                             std::placeholders::_1, true));
+    } else if (ConsumePrefix(&name, "tcpip:")) {
         std::string str(name);
 
         int port;
@@ -286,22 +289,22 @@
     }
 #endif
 
-    if (android::base::ConsumePrefix(&name, "dev:")) {
+    if (ConsumePrefix(&name, "dev:")) {
         return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
-    } else if (android::base::ConsumePrefix(&name, "jdwp:")) {
+    } else if (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")) {
+    } else if (ConsumePrefix(&name, "shell")) {
         return ShellService(name, transport);
-    } else if (android::base::ConsumePrefix(&name, "exec:")) {
+    } else if (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:")) {
+    } else if (ConsumePrefix(&name, "reverse:")) {
         return reverse_service(name, transport);
     } else if (name == "reconnect") {
         return create_service_thread(
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..889229f
--- /dev/null
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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, 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) {
+        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, 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, enable);
+
+    if (any_changed) {
+        WriteFdExactly(fd.get(), "Now reboot your device for settings to take effect\n");
+    }
+}
diff --git a/libmodprobe/libmodprobe_test.h b/adb/daemon/set_verity_enable_state_service.h
similarity index 75%
copy from libmodprobe/libmodprobe_test.h
copy to adb/daemon/set_verity_enable_state_service.h
index a001b69..c0ed98e 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/adb/daemon/set_verity_enable_state_service.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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.
@@ -16,8 +16,8 @@
 
 #pragma once
 
-#include <string>
-#include <vector>
+#include "adb_unique_fd.h"
 
-extern std::vector<std::string> test_modules;
-extern std::vector<std::string> modules_loaded;
+#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
index 0fb14c4..3c8f393 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -114,7 +114,7 @@
 namespace {
 
 // Reads from |fd| until close or failure.
-std::string ReadAll(borrowed_fd fd) {
+std::string ReadAll(int fd) {
     char buffer[512];
     std::string received;
 
@@ -222,7 +222,7 @@
 bool Subprocess::ForkAndExec(std::string* error) {
     unique_fd child_stdinout_sfd, child_stderr_sfd;
     unique_fd parent_error_sfd, child_error_sfd;
-    const char* pts_name = nullptr;
+    char pts_name[PATH_MAX];
 
     if (command_.empty()) {
         __android_log_security_bswrite(SEC_TAG_ADB_SHELL_INTERACTIVE, "");
@@ -283,22 +283,10 @@
     cenv.push_back(nullptr);
 
     if (type_ == SubprocessType::kPty) {
-        unique_fd pty_master(posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC));
-        if (pty_master == -1) {
-            *error =
-                    android::base::StringPrintf("failed to create pty master: %s", strerror(errno));
-            return false;
-        }
-        if (unlockpt(pty_master.get()) != 0) {
-            *error = android::base::StringPrintf("failed to unlockpt pty master: %s",
-                                                 strerror(errno));
-            return false;
-        }
-
-        pid_ = fork();
-        pts_name = ptsname(pty_master.get());
+        int fd;
+        pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
         if (pid_ > 0) {
-            stdinout_sfd_ = std::move(pty_master);
+          stdinout_sfd_.reset(fd);
         }
     } else {
         if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
@@ -329,10 +317,9 @@
             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);
+        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);
@@ -428,7 +415,7 @@
         }
     } else {
         // Raw protocol doesn't support multiple output streams, so combine stdout and stderr.
-        child_stderr_sfd.reset(dup(child_stdinout_sfd.get()));
+        child_stderr_sfd.reset(dup(child_stdinout_sfd));
     }
 
     D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(),
@@ -550,7 +537,7 @@
     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);
+            FD_SET(*sfd, &master_read_set);
         }
     }
 
@@ -560,8 +547,8 @@
         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);
+            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
@@ -586,7 +573,7 @@
 namespace {
 
 inline bool ValidAndInSet(const unique_fd& sfd, fd_set* set) {
-    return sfd != -1 && FD_ISSET(sfd.get(), set);
+    return sfd != -1 && FD_ISSET(sfd, set);
 }
 
 }   // namespace
@@ -594,8 +581,7 @@
 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;
+    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.
@@ -628,8 +614,8 @@
             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);
+                FD_CLR(protocol_sfd_, master_read_set_ptr);
+                FD_SET(stdinout_sfd_, master_write_set_ptr);
             }
         }
 
@@ -638,8 +624,8 @@
             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);
+                FD_SET(protocol_sfd_, master_read_set_ptr);
+                FD_CLR(stdinout_sfd_, master_write_set_ptr);
             }
         }
     }  // while (!dead_sfd)
@@ -653,7 +639,7 @@
         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();
+                PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
             }
             return &protocol_sfd_;
         }
@@ -669,7 +655,7 @@
                         ws.ws_col = cols;
                         ws.ws_xpixel = x_pixels;
                         ws.ws_ypixel = y_pixels;
-                        ioctl(stdinout_sfd_.get(), TIOCSWINSZ, &ws);
+                        ioctl(stdinout_sfd_, TIOCSWINSZ, &ws);
                     }
                     break;
                 case ShellProtocol::kIdStdin:
@@ -680,7 +666,8 @@
                         if (adb_shutdown(stdinout_sfd_, SHUT_WR) == 0) {
                             return nullptr;
                         }
-                        PLOG(ERROR) << "failed to shutdown writes to FD " << stdinout_sfd_.get();
+                        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
@@ -701,7 +688,7 @@
         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();
+                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.
@@ -721,14 +708,14 @@
         // 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();
+            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_.get();
+            PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_;
         }
         return &protocol_sfd_;
     }
diff --git a/adb/daemon/shell_service.h b/adb/daemon/shell_service.h
index 030228c..3abd958 100644
--- a/adb/daemon/shell_service.h
+++ b/adb/daemon/shell_service.h
@@ -48,7 +48,7 @@
 // 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);
+using Command = int(std::string_view args, int in, int out, int err);
 unique_fd StartCommandInProcess(std::string name, Command command, SubprocessProtocol protocol);
 
 // Create a pipe containing the error.
diff --git a/adb/daemon/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
index cdd8dbe..dc79d12 100644
--- a/adb/daemon/shell_service_test.cpp
+++ b/adb/daemon/shell_service_test.cpp
@@ -77,7 +77,7 @@
 namespace {
 
 // Reads raw data from |fd| until it closes or errors.
-std::string ReadRaw(borrowed_fd fd) {
+std::string ReadRaw(int fd) {
     char buffer[1024];
     char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
 
@@ -93,12 +93,12 @@
 // 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 ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
     int exit_code = -1;
     stdout->clear();
     stderr->clear();
 
-    auto protocol = std::make_unique<ShellProtocol>(fd.get());
+    auto protocol = std::make_unique<ShellProtocol>(fd);
     while (protocol->Read()) {
         switch (protocol->id()) {
             case ShellProtocol::kIdStdout:
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index 338d776..07b4ba8 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -37,12 +37,9 @@
 // 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;
@@ -67,34 +64,6 @@
     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.
@@ -106,10 +75,9 @@
     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));
 
+// clang-format off
 static struct func_desc fs_descriptors = {
     .intf = {
         .bLength = sizeof(fs_descriptors.intf),
@@ -204,13 +172,13 @@
 struct usb_ext_compat_desc os_desc_compat = {
     .bFirstInterfaceNumber = 0,
     .Reserved1 = cpu_to_le32(1),
-    .CompatibleID = { 'W', 'I', 'N', 'U', 'S', 'B', '\0', '\0'},
+    .CompatibleID = {0},
     .SubCompatibleID = {0},
     .Reserved2 = {0},
 };
 
 static struct usb_os_desc_header os_desc_header = {
-    .interface = cpu_to_le32(0),
+    .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),
@@ -218,14 +186,6 @@
     .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 {
@@ -261,14 +221,12 @@
     v2_descriptor.fs_count = 3;
     v2_descriptor.hs_count = 3;
     v2_descriptor.ss_count = 5;
-    v2_descriptor.os_count = 2;
+    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;
-    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;
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index f5893aa..1ba0de0 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -13,77 +13,27 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-java_library {
-    name: "deployagent_lib",
-    sdk_version: "24",
-    srcs: [
-        "deployagent/src/**/*.java",
-        "proto/**/*.proto",
-    ],
-    proto: {
-        type: "lite",
-    },
-}
 
 java_binary {
     name: "deployagent",
     sdk_version: "24",
-    static_libs: [
-        "deployagent_lib",
-    ],
+    srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
+    static_libs: ["apkzlib_zip"],
+    wrapper: "deployagent/deployagent.sh",
+    proto: {
+        type: "lite",
+    },
     dex_preopt: {
         enabled: false,
     }
 }
 
-android_test {
-    name: "FastDeployTests",
-
-    manifest: "AndroidManifest.xml",
-
-    srcs: [
-        "deployagent/test/com/android/fastdeploy/ApkArchiveTest.java",
-    ],
-
-    static_libs: [
-        "androidx.test.core",
-        "androidx.test.runner",
-        "androidx.test.rules",
-        "deployagent_lib",
-        "mockito-target-inline-minus-junit4",
-    ],
-
-    libs: [
-        "android.test.runner",
-        "android.test.base",
-        "android.test.mock",
-    ],
-
-    data: [
-        "testdata/sample.apk",
-        "testdata/sample.cd",
-    ],
-
-    optimize: {
-        enabled: false,
-    },
-}
-
-java_test_host {
-    name: "FastDeployHostTests",
-    srcs: [
-        "deployagent/test/com/android/fastdeploy/FastDeployTest.java",
-    ],
-    data: [
-        "testdata/helloworld5.apk",
-        "testdata/helloworld7.apk",
-    ],
-    libs: [
-        "compatibility-host-util",
-        "cts-tradefed",
-        "tradefed",
-    ],
-    test_suites: [
-        "general-tests",
-    ],
+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/AndroidManifest.xml b/adb/fastdeploy/AndroidManifest.xml
deleted file mode 100644
index 89dc745..0000000
--- a/adb/fastdeploy/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.fastdeploytests">
-
-    <application android:testOnly="true"
-                 android:debuggable="true">
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.fastdeploytests"
-        android:label="FastDeploy Tests" />
-</manifest>
\ No newline at end of file
diff --git a/adb/fastdeploy/AndroidTest.xml b/adb/fastdeploy/AndroidTest.xml
deleted file mode 100644
index 24a72bc..0000000
--- a/adb/fastdeploy/AndroidTest.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?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="Runs Device Tests for FastDeploy.">
-    <option name="test-suite-tag" value="FastDeployTests"/>
-
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true"/>
-        <option name="install-arg" value="-t"/>
-        <option name="test-file-name" value="FastDeployTests.apk"/>
-    </target_preparer>
-
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="false" />
-        <option name="push-file" key="sample.apk" value="/data/local/tmp/FastDeployTests/sample.apk" />
-        <option name="push-file" key="sample.cd" value="/data/local/tmp/FastDeployTests/sample.cd" />
-    </target_preparer>
-
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="com.android.fastdeploytests"/>
-        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
-    </test>
-
-    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
-        <option name="jar" value="FastDeployHostTests.jar" />
-    </test>
-</configuration>
diff --git a/adb/fastdeploy/deployagent/deployagent.sh b/adb/fastdeploy/deployagent/deployagent.sh
index 91576ca..4f17eb7 100755
--- a/adb/fastdeploy/deployagent/deployagent.sh
+++ b/adb/fastdeploy/deployagent/deployagent.sh
@@ -1,4 +1,7 @@
-#!/system/bin/sh
+# Script to start "deployagent" on the device, which has a very rudimentary
+# shell.
+#
 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/ApkArchive.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/ApkArchive.java
deleted file mode 100644
index 31e0502..0000000
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/ApkArchive.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * 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.fastdeploy;
-
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
-
-/**
- * Extremely light-weight APK parser class.
- * Aware of Central Directory, Local File Headers and Signature.
- * No Zip64 support yet.
- */
-public final class ApkArchive {
-    private static final String TAG = "ApkArchive";
-
-    // Central Directory constants.
-    private static final int EOCD_SIGNATURE = 0x06054b50;
-    private static final int EOCD_MIN_SIZE = 22;
-    private static final long EOCD_MAX_SIZE = 65_535L + EOCD_MIN_SIZE;
-
-    private static final int CD_ENTRY_HEADER_SIZE_BYTES = 22;
-    private static final int CD_LOCAL_FILE_HEADER_SIZE_OFFSET = 12;
-
-    // Signature constants.
-    private static final int EOSIGNATURE_SIZE = 24;
-
-    public final static class Dump {
-        final byte[] cd;
-        final byte[] signature;
-
-        Dump(byte[] cd, byte[] signature) {
-            this.cd = cd;
-            this.signature = signature;
-        }
-    }
-
-    final static class Location {
-        final long offset;
-        final long size;
-
-        public Location(long offset, long size) {
-            this.offset = offset;
-            this.size = size;
-        }
-    }
-
-    private final RandomAccessFile mFile;
-    private final FileChannel mChannel;
-
-    public ApkArchive(File apk) throws IOException {
-        mFile = new RandomAccessFile(apk, "r");
-        mChannel = mFile.getChannel();
-    }
-
-    /**
-     * Extract the APK metadata: content of Central Directory and Signature.
-     *
-     * @return raw content from APK representing CD and Signature data.
-     */
-    public Dump extractMetadata() throws IOException {
-        Location cdLoc = getCDLocation();
-        byte[] cd = readMetadata(cdLoc);
-
-        byte[] signature = null;
-        Location sigLoc = getSignatureLocation(cdLoc.offset);
-        if (sigLoc != null) {
-            signature = readMetadata(sigLoc);
-            long size = ByteBuffer.wrap(signature).order(ByteOrder.LITTLE_ENDIAN).getLong();
-            if (sigLoc.size != size) {
-                Log.e(TAG, "Mismatching signature sizes: " + sigLoc.size + " != " + size);
-                signature = null;
-            }
-        }
-
-        return new Dump(cd, signature);
-    }
-
-    private long findEndOfCDRecord() throws IOException {
-        final long fileSize = mChannel.size();
-        int sizeToRead = Math.toIntExact(Math.min(fileSize, EOCD_MAX_SIZE));
-        final long readOffset = fileSize - sizeToRead;
-        ByteBuffer buffer = mChannel.map(FileChannel.MapMode.READ_ONLY, readOffset,
-                sizeToRead).order(ByteOrder.LITTLE_ENDIAN);
-
-        buffer.position(sizeToRead - EOCD_MIN_SIZE);
-        while (true) {
-            int signature = buffer.getInt(); // Read 4 bytes.
-            if (signature == EOCD_SIGNATURE) {
-                return readOffset + buffer.position() - 4;
-            }
-            if (buffer.position() == 4) {
-                break;
-            }
-            buffer.position(buffer.position() - Integer.BYTES - 1); // Backtrack 5 bytes.
-        }
-
-        return -1L;
-    }
-
-    private Location findCDRecord(ByteBuffer buf) {
-        if (buf.order() != ByteOrder.LITTLE_ENDIAN) {
-            throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
-        }
-        if (buf.remaining() < CD_ENTRY_HEADER_SIZE_BYTES) {
-            throw new IllegalArgumentException(
-                    "Input too short. Need at least " + CD_ENTRY_HEADER_SIZE_BYTES
-                            + " bytes, available: " + buf.remaining() + "bytes.");
-        }
-
-        int originalPosition = buf.position();
-        int recordSignature = buf.getInt();
-        if (recordSignature != EOCD_SIGNATURE) {
-            throw new IllegalArgumentException(
-                    "Not a Central Directory record. Signature: 0x"
-                            + Long.toHexString(recordSignature & 0xffffffffL));
-        }
-
-        buf.position(originalPosition + CD_LOCAL_FILE_HEADER_SIZE_OFFSET);
-        long size = buf.getInt() & 0xffffffffL;
-        long offset = buf.getInt() & 0xffffffffL;
-        return new Location(offset, size);
-    }
-
-    // Retrieve the location of the Central Directory Record.
-    Location getCDLocation() throws IOException {
-        long eocdRecord = findEndOfCDRecord();
-        if (eocdRecord < 0) {
-            throw new IllegalArgumentException("Unable to find End of Central Directory record.");
-        }
-
-        Location location = findCDRecord(mChannel.map(FileChannel.MapMode.READ_ONLY, eocdRecord,
-                CD_ENTRY_HEADER_SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN));
-        if (location == null) {
-            throw new IllegalArgumentException("Unable to find Central Directory File Header.");
-        }
-
-        return location;
-    }
-
-    // Retrieve the location of the signature block starting from Central
-    // Directory Record or null if signature is not found.
-    Location getSignatureLocation(long cdRecordOffset) throws IOException {
-        long signatureOffset = cdRecordOffset - EOSIGNATURE_SIZE;
-        if (signatureOffset < 0) {
-            Log.e(TAG, "Unable to find Signature.");
-            return null;
-        }
-
-        ByteBuffer signature = mChannel.map(FileChannel.MapMode.READ_ONLY, signatureOffset,
-                EOSIGNATURE_SIZE).order(ByteOrder.LITTLE_ENDIAN);
-
-        long size = signature.getLong();
-
-        byte[] sign = new byte[16];
-        signature.get(sign);
-        String signAsString = new String(sign);
-        if (!"APK Sig Block 42".equals(signAsString)) {
-            Log.e(TAG, "Signature magic does not match: " + signAsString);
-            return null;
-        }
-
-        long offset = cdRecordOffset - size - 8;
-
-        return new Location(offset, size);
-    }
-
-    private byte[] readMetadata(Location loc) throws IOException {
-        byte[] payload = new byte[(int) loc.size];
-        ByteBuffer buffer = mChannel.map(FileChannel.MapMode.READ_ONLY, loc.offset, loc.size);
-        buffer.get(payload);
-        return payload;
-    }
-}
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
index 3812307..2d3b135 100644
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -24,22 +24,18 @@
 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.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.nio.channels.WritableByteChannel;
+import java.util.Set;
 
-import com.android.fastdeploy.PatchFormatException;
-import com.android.fastdeploy.ApkArchive;
-import com.android.fastdeploy.APKDump;
 import com.android.fastdeploy.APKMetaData;
 import com.android.fastdeploy.PatchUtils;
 
-import com.google.protobuf.ByteString;
-
 public final class DeployAgent {
     private static final int BUFFER_SIZE = 128 * 1024;
-    private static final int AGENT_VERSION = 0x00000003;
+    private static final int AGENT_VERSION = 0x00000002;
 
     public static void main(String[] args) {
         int exitCode = 0;
@@ -49,70 +45,68 @@
             }
 
             String commandString = args[0];
-            switch (commandString) {
-                case "dump": {
-                    if (args.length != 3) {
-                        showUsage(1);
-                    }
 
-                    String requiredVersion = args[1];
-                    if (AGENT_VERSION == Integer.parseInt(requiredVersion)) {
-                        String packageName = args[2];
-                        String packagePath = getFilenameFromPackageName(packageName);
-                        if (packagePath != null) {
-                            dumpApk(packageName, packagePath);
-                        } else {
-                            exitCode = 3;
-                        }
-                    } else {
-                        System.out.printf("0x%08X\n", AGENT_VERSION);
-                        exitCode = 4;
-                    }
-                    break;
-                }
-                case "apply": {
-                    if (args.length < 3) {
-                        showUsage(1);
-                    }
-
-                    String patchPath = args[1];
-                    String outputParam = args[2];
-
-                    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 > 3) {
-                            String outputPath = args[3];
-                            if (!outputPath.equals("-")) {
-                                outputStream = new FileOutputStream(outputPath);
-                            }
-                        }
-                        if (outputStream == null) {
-                            outputStream = System.out;
-                        }
-                        writePatchToStream(deltaInputStream, outputStream);
-                    } else if (outputParam.equals("-pm")) {
-                        String[] sessionArgs = null;
-                        if (args.length > 3) {
-                            int numSessionArgs = args.length - 3;
-                            sessionArgs = new String[numSessionArgs];
-                            for (int i = 0; i < numSessionArgs; i++) {
-                                sessionArgs[i] = args[i + 3];
-                            }
-                        }
-                        exitCode = applyPatch(deltaInputStream, sessionArgs);
-                    }
-                    break;
-                }
-                default:
+            if (commandString.equals("extract")) {
+                if (args.length != 2) {
                     showUsage(1);
-                    break;
+                }
+
+                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);
@@ -124,16 +118,16 @@
 
     private static void showUsage(int exitCode) {
         System.err.println(
-                "usage: deployagent <command> [<args>]\n\n" +
-                        "commands:\n" +
-                        "dump VERSION PKGNAME  dump info for an installed package given that " +
-                        "VERSION equals current agent's version\n" +
-                        "apply 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"
-        );
+            "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);
     }
 
@@ -168,34 +162,32 @@
                 }
                 int equalsIndex = line.lastIndexOf(packageSuffix);
                 String fileName =
-                        line.substring(packageIndex + packagePrefix.length(), equalsIndex);
+                    line.substring(packageIndex + packagePrefix.length(), equalsIndex);
                 return fileName;
             }
         }
         return null;
     }
 
-    private static void dumpApk(String packageName, String packagePath) throws IOException {
-        File apk = new File(packagePath);
-        ApkArchive.Dump dump = new ApkArchive(apk).extractMetadata();
-
-        APKDump.Builder apkDumpBuilder = APKDump.newBuilder();
-        apkDumpBuilder.setName(packageName);
-        if (dump.cd != null) {
-            apkDumpBuilder.setCd(ByteString.copyFrom(dump.cd));
+    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");
         }
-        if (dump.signature != null) {
-            apkDumpBuilder.setSignature(ByteString.copyFrom(dump.signature));
-        }
-        apkDumpBuilder.setAbsolutePath(apk.getAbsolutePath());
+        return new File(filename);
+    }
 
-        apkDumpBuilder.build().writeTo(System.out);
+    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++) {
+        for (int i=0 ; args != null && i<args.length ; i++) {
             commandBuilder.append(args[i] + " ");
         }
 
@@ -207,8 +199,7 @@
         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 Integer.parseInt(line.substring(successLineStart.length(), line.lastIndexOf(successLineEnd)));
             }
         }
 
@@ -222,15 +213,17 @@
         return p.exitValue();
     }
 
-    private static int applyPatch(InputStream deltaStream, String[] sessionArgs)
+    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(deltaStream, sessionId);
+        int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
+
         if (writeExitCode == 0) {
             return commitInstallSession(sessionId);
         } else {
@@ -238,94 +231,85 @@
         }
     }
 
-    private static long writePatchToStream(InputStream patchData,
-            OutputStream outputStream) throws IOException, PatchFormatException {
+    private static long writePatchToStream(RandomAccessFile oldData, InputStream patchData,
+        OutputStream outputStream) throws IOException, PatchFormatException {
         long newSize = readPatchHeader(patchData);
-        long bytesWritten = writePatchedDataToStream(newSize, patchData, outputStream);
+        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));
+                "output size mismatch (expected %ld but wrote %ld)", newSize, bytesWritten));
         }
         return bytesWritten;
     }
 
     private static long readPatchHeader(InputStream patchData)
-            throws IOException, PatchFormatException {
+        throws IOException, PatchFormatException {
         byte[] signatureBuffer = new byte[PatchUtils.SIGNATURE.length()];
         try {
-            PatchUtils.readFully(patchData, signatureBuffer);
+            PatchUtils.readFully(patchData, signatureBuffer, 0, signatureBuffer.length);
         } catch (IOException e) {
             throw new PatchFormatException("truncated signature");
         }
 
-        String signature = new String(signatureBuffer);
+        String signature = new String(signatureBuffer, 0, signatureBuffer.length, "US-ASCII");
         if (!PatchUtils.SIGNATURE.equals(signature)) {
             throw new PatchFormatException("bad signature");
         }
 
-        long newSize = PatchUtils.readLELong(patchData);
-        if (newSize < 0) {
-            throw new PatchFormatException("bad newSize: " + newSize);
+        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(long newSize, InputStream patchData,
-            OutputStream outputStream) throws IOException {
-        String deviceFile = PatchUtils.readString(patchData);
-        RandomAccessFile oldDataFile = new RandomAccessFile(deviceFile, "r");
-        FileChannel oldData = oldDataFile.getChannel();
-
-        WritableByteChannel newData = Channels.newChannel(outputStream);
-
+    // (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 newDataLen = PatchUtils.readLELong(patchData);
-            if (newDataLen > 0) {
-                PatchUtils.pipe(patchData, outputStream, buffer, newDataLen);
+            long copyLen = PatchUtils.readFormattedLong(patchData);
+            if (copyLen > 0) {
+                PatchUtils.pipe(patchData, outputStream, buffer, (int) copyLen);
             }
 
-            long oldDataOffset = PatchUtils.readLELong(patchData);
-            long oldDataLen = PatchUtils.readLELong(patchData);
-            if (oldDataLen >= 0) {
-                long offset = oldDataOffset;
-                long len = oldDataLen;
-                while (len > 0) {
-                    long chunkLen = Math.min(len, 1024*1024*1024);
-                    oldData.transferTo(offset, chunkLen, newData);
-                    offset += chunkLen;
-                    len -= chunkLen;
-                }
+            long oldDataOffset = PatchUtils.readFormattedLong(patchData);
+            long oldDataLen = PatchUtils.readFormattedLong(patchData);
+            oldData.seek(oldDataOffset);
+            if (oldDataLen > 0) {
+                PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
             }
-            newDataBytesWritten += newDataLen + oldDataLen;
+
+            newDataBytesWritten += copyLen + oldDataLen;
         }
 
         return newDataBytesWritten;
     }
 
-    private static int writePatchedDataToSession(InputStream patchData, int sessionId)
+    private static int writePatchedDataToSession(RandomAccessFile oldData, InputStream patchData, int sessionId)
             throws IOException, PatchFormatException {
         try {
             Process p;
             long newSize = readPatchHeader(patchData);
-            String command = String.format("pm install-write -S %d %d -- -", newSize, sessionId);
+            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(newSize, patchData, sessionOutputStream);
+            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));
+                        String.format("output size mismatch (expected %d but wrote %)", newSize, bytesWritten));
             }
             return p.exitValue();
         } catch (InterruptedException e) {
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java
deleted file mode 100644
index 54be26f..0000000
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java
+++ /dev/null
@@ -1,74 +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.
- */
-
-package com.android.fastdeploy;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-class PatchUtils {
-    public static final String SIGNATURE = "FASTDEPLOY";
-
-    /**
-     * Reads a 64-bit signed integer in Little Endian format from the specified {@link
-     * DataInputStream}.
-     *
-     * @param in the stream to read from.
-     */
-    static long readLELong(InputStream in) throws IOException {
-        byte[] buffer = new byte[Long.BYTES];
-        readFully(in, buffer);
-        ByteBuffer buf = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
-        return buf.getLong();
-    }
-
-    static String readString(InputStream in) throws IOException {
-        int size = (int) readLELong(in);
-        byte[] buffer = new byte[size];
-        readFully(in, buffer);
-        return new String(buffer);
-    }
-
-    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 readFully(final InputStream in, final byte[] destination) throws IOException {
-        readFully(in, destination, 0, destination.length);
-    }
-
-    static void pipe(final InputStream in, final OutputStream out, final byte[] buffer,
-            long copyLength) throws IOException {
-        while (copyLength > 0) {
-            int maxCopy = (int) Math.min(buffer.length, copyLength);
-            readFully(in, buffer, 0, maxCopy);
-            out.write(buffer, 0, maxCopy);
-            copyLength -= maxCopy;
-        }
-    }
-}
diff --git a/adb/fastdeploy/deployagent/test/com/android/fastdeploy/ApkArchiveTest.java b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/ApkArchiveTest.java
deleted file mode 100644
index 7c2468f..0000000
--- a/adb/fastdeploy/deployagent/test/com/android/fastdeploy/ApkArchiveTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.fastdeploy;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.android.fastdeploy.ApkArchive;
-
-import java.io.File;
-import java.io.IOException;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ApkArchiveTest {
-    private static final File SAMPLE_APK = new File("/data/local/tmp/FastDeployTests/sample.apk");
-    private static final File WRONG_APK = new File("/data/local/tmp/FastDeployTests/sample.cd");
-
-    @Test
-    public void testApkArchiveSizes() throws IOException {
-        ApkArchive archive = new ApkArchive(SAMPLE_APK);
-
-        ApkArchive.Location cdLoc = archive.getCDLocation();
-        assertNotEquals(cdLoc, null);
-        assertEquals(cdLoc.offset, 2044145);
-        assertEquals(cdLoc.size, 49390);
-
-        // Check that block can be retrieved
-        ApkArchive.Location sigLoc = archive.getSignatureLocation(cdLoc.offset);
-        assertNotEquals(sigLoc, null);
-        assertEquals(sigLoc.offset, 2040049);
-        assertEquals(sigLoc.size, 4088);
-    }
-
-    @Test
-    public void testApkArchiveDump() throws IOException {
-        ApkArchive archive = new ApkArchive(SAMPLE_APK);
-
-        ApkArchive.Dump dump = archive.extractMetadata();
-        assertNotEquals(dump, null);
-        assertNotEquals(dump.cd, null);
-        assertNotEquals(dump.signature, null);
-        assertEquals(dump.cd.length, 49390);
-        assertEquals(dump.signature.length, 4088);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testApkArchiveDumpWrongApk() throws IOException {
-        ApkArchive archive = new ApkArchive(WRONG_APK);
-
-        archive.extractMetadata();
-    }
-}
diff --git a/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java
deleted file mode 100644
index 4aa2f79..0000000
--- a/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java
+++ /dev/null
@@ -1,89 +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.
- */
-
-package com.android.fastdeploy;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class FastDeployTest extends BaseHostJUnit4Test {
-
-    private static final String TEST_APP_PACKAGE = "com.example.helloworld";
-    private static final String TEST_APK5_NAME = "helloworld5.apk";
-    private static final String TEST_APK7_NAME = "helloworld7.apk";
-
-    private String mTestApk5Path;
-    private String mTestApk7Path;
-
-    @Before
-    public void setUp() throws Exception {
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
-        getDevice().uninstallPackage(TEST_APP_PACKAGE);
-        mTestApk5Path = buildHelper.getTestFile(TEST_APK5_NAME).getAbsolutePath();
-        mTestApk7Path = buildHelper.getTestFile(TEST_APK7_NAME).getAbsolutePath();
-    }
-
-    @Test
-    public void testAppInstalls() throws Exception {
-        fastInstallPackage(mTestApk5Path);
-        assertTrue(isAppInstalled(TEST_APP_PACKAGE));
-        getDevice().uninstallPackage(TEST_APP_PACKAGE);
-        assertFalse(isAppInstalled(TEST_APP_PACKAGE));
-    }
-
-    @Test
-    public void testAppPatch() throws Exception {
-        fastInstallPackage(mTestApk5Path);
-        assertTrue(isAppInstalled(TEST_APP_PACKAGE));
-        fastInstallPackage(mTestApk7Path);
-        assertTrue(isAppInstalled(TEST_APP_PACKAGE));
-        getDevice().uninstallPackage(TEST_APP_PACKAGE);
-        assertFalse(isAppInstalled(TEST_APP_PACKAGE));
-    }
-
-    private boolean isAppInstalled(String packageName) throws DeviceNotAvailableException {
-        final String result = getDevice().executeShellCommand("pm list packages");
-        CLog.logAndDisplay(LogLevel.INFO, result);
-        final int prefixLength = "package:".length();
-        return Arrays.stream(result.split("\\r?\\n"))
-                .anyMatch(line -> line.substring(prefixLength).equals(packageName));
-    }
-
-    // Mostly copied from PkgInstallSignatureVerificationTest.java.
-    private void fastInstallPackage(String apkPath)
-            throws IOException, DeviceNotAvailableException {
-        String result = getDevice().executeAdbCommand("install", "-t", "--fastdeploy", "--force-agent",
-                apkPath);
-        CLog.logAndDisplay(LogLevel.INFO, result);
-    }
-}
-
-
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchFormatException.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
similarity index 100%
rename from adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchFormatException.java
rename to adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
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/apk_archive.cpp b/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
deleted file mode 100644
index 3dc5e50..0000000
--- a/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * 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 ADB
-
-#include "apk_archive.h"
-
-#include "adb_trace.h"
-#include "sysdeps.h"
-
-#include <android-base/endian.h>
-#include <android-base/mapped_file.h>
-
-#include <openssl/md5.h>
-
-constexpr uint16_t kCompressStored = 0;
-
-// mask value that signifies that the entry has a DD
-static const uint32_t kGPBDDFlagMask = 0x0008;
-
-namespace {
-struct FileRegion {
-    FileRegion(borrowed_fd fd, off64_t offset, size_t length)
-        : mapped_(android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), offset, length,
-                                                          PROT_READ)) {
-        if (mapped_.data() != nullptr) {
-            return;
-        }
-
-        // Mapped file failed, falling back to pread.
-        buffer_.resize(length);
-        if (auto err = adb_pread(fd.get(), buffer_.data(), length, offset); size_t(err) != length) {
-            fprintf(stderr, "Unable to read %lld bytes at offset %" PRId64 " \n",
-                    static_cast<long long>(length), offset);
-            buffer_.clear();
-            return;
-        }
-    }
-
-    const char* data() const { return mapped_.data() ? mapped_.data() : buffer_.data(); }
-    size_t size() const { return mapped_.data() ? mapped_.size() : buffer_.size(); }
-
-  private:
-    FileRegion() = default;
-    DISALLOW_COPY_AND_ASSIGN(FileRegion);
-
-    android::base::MappedFile mapped_;
-    std::string buffer_;
-};
-}  // namespace
-
-using com::android::fastdeploy::APKDump;
-
-ApkArchive::ApkArchive(const std::string& path) : path_(path), size_(0) {
-    fd_.reset(adb_open(path_.c_str(), O_RDONLY));
-    if (fd_ == -1) {
-        fprintf(stderr, "Unable to open file '%s'\n", path_.c_str());
-        return;
-    }
-
-    struct stat st;
-    if (stat(path_.c_str(), &st) == -1) {
-        fprintf(stderr, "Unable to stat file '%s'\n", path_.c_str());
-        return;
-    }
-    size_ = st.st_size;
-}
-
-ApkArchive::~ApkArchive() {}
-
-APKDump ApkArchive::ExtractMetadata() {
-    D("ExtractMetadata");
-    if (!ready()) {
-        return {};
-    }
-
-    Location cdLoc = GetCDLocation();
-    if (!cdLoc.valid) {
-        return {};
-    }
-
-    APKDump dump;
-    dump.set_absolute_path(path_);
-    dump.set_cd(ReadMetadata(cdLoc));
-
-    Location sigLoc = GetSignatureLocation(cdLoc.offset);
-    if (sigLoc.valid) {
-        dump.set_signature(ReadMetadata(sigLoc));
-    }
-    return dump;
-}
-
-off_t ApkArchive::FindEndOfCDRecord() const {
-    constexpr int endOfCDSignature = 0x06054b50;
-    constexpr off_t endOfCDMinSize = 22;
-    constexpr off_t endOfCDMaxSize = 65535 + endOfCDMinSize;
-
-    auto sizeToRead = std::min(size_, endOfCDMaxSize);
-    auto readOffset = size_ - sizeToRead;
-    FileRegion mapped(fd_, readOffset, sizeToRead);
-
-    // Start scanning from the end
-    auto* start = mapped.data();
-    auto* cursor = start + mapped.size() - sizeof(endOfCDSignature);
-
-    // Search for End of Central Directory record signature.
-    while (cursor >= start) {
-        if (*(int32_t*)cursor == endOfCDSignature) {
-            return readOffset + (cursor - start);
-        }
-        cursor--;
-    }
-    return -1;
-}
-
-ApkArchive::Location ApkArchive::FindCDRecord(const char* cursor) {
-    struct ecdr_t {
-        int32_t signature;
-        uint16_t diskNumber;
-        uint16_t numDisk;
-        uint16_t diskEntries;
-        uint16_t numEntries;
-        uint32_t crSize;
-        uint32_t offsetToCdHeader;
-        uint16_t commentSize;
-        uint8_t comment[0];
-    } __attribute__((packed));
-    ecdr_t* header = (ecdr_t*)cursor;
-
-    Location location;
-    location.offset = header->offsetToCdHeader;
-    location.size = header->crSize;
-    location.valid = true;
-    return location;
-}
-
-ApkArchive::Location ApkArchive::GetCDLocation() {
-    constexpr off_t cdEntryHeaderSizeBytes = 22;
-    Location location;
-
-    // Find End of Central Directory Record
-    off_t eocdRecord = FindEndOfCDRecord();
-    if (eocdRecord < 0) {
-        fprintf(stderr, "Unable to find End of Central Directory record in file '%s'\n",
-                path_.c_str());
-        return location;
-    }
-
-    // Find Central Directory Record
-    FileRegion mapped(fd_, eocdRecord, cdEntryHeaderSizeBytes);
-    location = FindCDRecord(mapped.data());
-    if (!location.valid) {
-        fprintf(stderr, "Unable to find Central Directory File Header in file '%s'\n",
-                path_.c_str());
-        return location;
-    }
-
-    return location;
-}
-
-ApkArchive::Location ApkArchive::GetSignatureLocation(off_t cdRecordOffset) {
-    Location location;
-
-    // Signature constants.
-    constexpr off_t endOfSignatureSize = 24;
-    off_t signatureOffset = cdRecordOffset - endOfSignatureSize;
-    if (signatureOffset < 0) {
-        fprintf(stderr, "Unable to find signature in file '%s'\n", path_.c_str());
-        return location;
-    }
-
-    FileRegion mapped(fd_, signatureOffset, endOfSignatureSize);
-
-    uint64_t signatureSize = *(uint64_t*)mapped.data();
-    auto* signature = mapped.data() + sizeof(signatureSize);
-    // Check if there is a v2/v3 Signature block here.
-    if (memcmp(signature, "APK Sig Block 42", 16)) {
-        return location;
-    }
-
-    // This is likely a signature block.
-    location.size = signatureSize;
-    location.offset = cdRecordOffset - location.size - 8;
-    location.valid = true;
-
-    return location;
-}
-
-std::string ApkArchive::ReadMetadata(Location loc) const {
-    FileRegion mapped(fd_, loc.offset, loc.size);
-    return {mapped.data(), mapped.size()};
-}
-
-size_t ApkArchive::ParseCentralDirectoryRecord(const char* input, size_t size, std::string* md5Hash,
-                                               int64_t* localFileHeaderOffset, int64_t* dataSize) {
-    // A structure representing the fixed length fields for a single
-    // record in the central directory of the archive. In addition to
-    // the fixed length fields listed here, each central directory
-    // record contains a variable length "file_name" and "extra_field"
-    // whose lengths are given by |file_name_length| and |extra_field_length|
-    // respectively.
-    static constexpr int kCDFileHeaderMagic = 0x02014b50;
-    struct CentralDirectoryRecord {
-        // The start of record signature. Must be |kSignature|.
-        uint32_t record_signature;
-        // Source tool version. Top byte gives source OS.
-        uint16_t version_made_by;
-        // Tool version. Ignored by this implementation.
-        uint16_t version_needed;
-        // The "general purpose bit flags" for this entry. The only
-        // flag value that we currently check for is the "data descriptor"
-        // flag.
-        uint16_t gpb_flags;
-        // The compression method for this entry, one of |kCompressStored|
-        // and |kCompressDeflated|.
-        uint16_t compression_method;
-        // The file modification time and date for this entry.
-        uint16_t last_mod_time;
-        uint16_t last_mod_date;
-        // The CRC-32 checksum for this entry.
-        uint32_t crc32;
-        // The compressed size (in bytes) of this entry.
-        uint32_t compressed_size;
-        // The uncompressed size (in bytes) of this entry.
-        uint32_t uncompressed_size;
-        // The length of the entry file name in bytes. The file name
-        // will appear immediately after this record.
-        uint16_t file_name_length;
-        // The length of the extra field info (in bytes). This data
-        // will appear immediately after the entry file name.
-        uint16_t extra_field_length;
-        // The length of the entry comment (in bytes). This data will
-        // appear immediately after the extra field.
-        uint16_t comment_length;
-        // The start disk for this entry. Ignored by this implementation).
-        uint16_t file_start_disk;
-        // File attributes. Ignored by this implementation.
-        uint16_t internal_file_attributes;
-        // File attributes. For archives created on Unix, the top bits are the
-        // mode.
-        uint32_t external_file_attributes;
-        // The offset to the local file header for this entry, from the
-        // beginning of this archive.
-        uint32_t local_file_header_offset;
-
-      private:
-        CentralDirectoryRecord() = default;
-        DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
-    } __attribute__((packed));
-
-    const CentralDirectoryRecord* cdr;
-    if (size < sizeof(*cdr)) {
-        return 0;
-    }
-
-    auto begin = input;
-    cdr = reinterpret_cast<const CentralDirectoryRecord*>(begin);
-    if (cdr->record_signature != kCDFileHeaderMagic) {
-        fprintf(stderr, "Invalid Central Directory Record signature\n");
-        return 0;
-    }
-    auto end = begin + sizeof(*cdr) + cdr->file_name_length + cdr->extra_field_length +
-               cdr->comment_length;
-
-    uint8_t md5Digest[MD5_DIGEST_LENGTH];
-    MD5((const unsigned char*)begin, end - begin, md5Digest);
-    md5Hash->assign((const char*)md5Digest, sizeof(md5Digest));
-
-    *localFileHeaderOffset = cdr->local_file_header_offset;
-    *dataSize = (cdr->compression_method == kCompressStored) ? cdr->uncompressed_size
-                                                             : cdr->compressed_size;
-
-    return end - begin;
-}
-
-size_t ApkArchive::CalculateLocalFileEntrySize(int64_t localFileHeaderOffset,
-                                               int64_t dataSize) const {
-    // The local file header for a given entry. This duplicates information
-    // present in the central directory of the archive. It is an error for
-    // the information here to be different from the central directory
-    // information for a given entry.
-    static constexpr int kLocalFileHeaderMagic = 0x04034b50;
-    struct LocalFileHeader {
-        // The local file header signature, must be |kSignature|.
-        uint32_t lfh_signature;
-        // Tool version. Ignored by this implementation.
-        uint16_t version_needed;
-        // The "general purpose bit flags" for this entry. The only
-        // flag value that we currently check for is the "data descriptor"
-        // flag.
-        uint16_t gpb_flags;
-        // The compression method for this entry, one of |kCompressStored|
-        // and |kCompressDeflated|.
-        uint16_t compression_method;
-        // The file modification time and date for this entry.
-        uint16_t last_mod_time;
-        uint16_t last_mod_date;
-        // The CRC-32 checksum for this entry.
-        uint32_t crc32;
-        // The compressed size (in bytes) of this entry.
-        uint32_t compressed_size;
-        // The uncompressed size (in bytes) of this entry.
-        uint32_t uncompressed_size;
-        // The length of the entry file name in bytes. The file name
-        // will appear immediately after this record.
-        uint16_t file_name_length;
-        // The length of the extra field info (in bytes). This data
-        // will appear immediately after the entry file name.
-        uint16_t extra_field_length;
-
-      private:
-        LocalFileHeader() = default;
-        DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
-    } __attribute__((packed));
-    static constexpr int kLocalFileHeaderSize = sizeof(LocalFileHeader);
-    CHECK(ready()) << path_;
-
-    const LocalFileHeader* lfh;
-    if (localFileHeaderOffset + kLocalFileHeaderSize > size_) {
-        fprintf(stderr,
-                "Invalid Local File Header offset in file '%s' at offset %lld, file size %lld\n",
-                path_.c_str(), static_cast<long long>(localFileHeaderOffset),
-                static_cast<long long>(size_));
-        return 0;
-    }
-
-    FileRegion lfhMapped(fd_, localFileHeaderOffset, sizeof(LocalFileHeader));
-    lfh = reinterpret_cast<const LocalFileHeader*>(lfhMapped.data());
-    if (lfh->lfh_signature != kLocalFileHeaderMagic) {
-        fprintf(stderr, "Invalid Local File Header signature in file '%s' at offset %lld\n",
-                path_.c_str(), static_cast<long long>(localFileHeaderOffset));
-        return 0;
-    }
-
-    // The *optional* data descriptor start signature.
-    static constexpr int kOptionalDataDescriptorMagic = 0x08074b50;
-    struct DataDescriptor {
-        // CRC-32 checksum of the entry.
-        uint32_t crc32;
-        // Compressed size of the entry.
-        uint32_t compressed_size;
-        // Uncompressed size of the entry.
-        uint32_t uncompressed_size;
-
-      private:
-        DataDescriptor() = default;
-        DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
-    } __attribute__((packed));
-    static constexpr int kDataDescriptorSize = sizeof(DataDescriptor);
-
-    off_t ddOffset = localFileHeaderOffset + kLocalFileHeaderSize + lfh->file_name_length +
-                     lfh->extra_field_length + dataSize;
-    int64_t ddSize = 0;
-
-    int64_t localDataSize;
-    if (lfh->gpb_flags & kGPBDDFlagMask) {
-        // There is trailing data descriptor.
-        const DataDescriptor* dd;
-
-        if (ddOffset + int(sizeof(uint32_t)) > size_) {
-            fprintf(stderr,
-                    "Error reading trailing data descriptor signature in file '%s' at offset %lld, "
-                    "file size %lld\n",
-                    path_.c_str(), static_cast<long long>(ddOffset), static_cast<long long>(size_));
-            return 0;
-        }
-
-        FileRegion ddMapped(fd_, ddOffset, sizeof(uint32_t) + sizeof(DataDescriptor));
-
-        off_t localDDOffset = 0;
-        if (kOptionalDataDescriptorMagic == *(uint32_t*)ddMapped.data()) {
-            ddOffset += sizeof(uint32_t);
-            localDDOffset += sizeof(uint32_t);
-            ddSize += sizeof(uint32_t);
-        }
-        if (ddOffset + kDataDescriptorSize > size_) {
-            fprintf(stderr,
-                    "Error reading trailing data descriptor in file '%s' at offset %lld, file size "
-                    "%lld\n",
-                    path_.c_str(), static_cast<long long>(ddOffset), static_cast<long long>(size_));
-            return 0;
-        }
-
-        dd = reinterpret_cast<const DataDescriptor*>(ddMapped.data() + localDDOffset);
-        localDataSize = (lfh->compression_method == kCompressStored) ? dd->uncompressed_size
-                                                                     : dd->compressed_size;
-        ddSize += sizeof(*dd);
-    } else {
-        localDataSize = (lfh->compression_method == kCompressStored) ? lfh->uncompressed_size
-                                                                     : lfh->compressed_size;
-    }
-    if (localDataSize != dataSize) {
-        fprintf(stderr,
-                "Data sizes mismatch in file '%s' at offset %lld, CDr: %lld vs LHR/DD: %lld\n",
-                path_.c_str(), static_cast<long long>(localFileHeaderOffset),
-                static_cast<long long>(dataSize), static_cast<long long>(localDataSize));
-        return 0;
-    }
-
-    return kLocalFileHeaderSize + lfh->file_name_length + lfh->extra_field_length + dataSize +
-           ddSize;
-}
diff --git a/adb/fastdeploy/deploypatchgenerator/apk_archive.h b/adb/fastdeploy/deploypatchgenerator/apk_archive.h
deleted file mode 100644
index 7127800..0000000
--- a/adb/fastdeploy/deploypatchgenerator/apk_archive.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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 <string>
-#include <vector>
-
-#include <adb_unique_fd.h>
-
-#include "fastdeploy/proto/ApkEntry.pb.h"
-
-class ApkArchiveTester;
-
-// Manipulates an APK archive. Process it by mmaping it in order to minimize
-// I/Os.
-class ApkArchive {
-  public:
-    friend ApkArchiveTester;
-
-    // A convenience struct to store the result of search operation when
-    // locating the EoCDr, CDr, and Signature Block.
-    struct Location {
-        off_t offset = 0;
-        off_t size = 0;
-        bool valid = false;
-    };
-
-    ApkArchive(const std::string& path);
-    ~ApkArchive();
-
-    com::android::fastdeploy::APKDump ExtractMetadata();
-
-    // Parses the CDr starting from |input| and returns number of bytes consumed.
-    // Extracts local file header offset, data size and calculates MD5 hash of the record.
-    // 0 indicates invalid CDr.
-    static size_t ParseCentralDirectoryRecord(const char* input, size_t size, std::string* md5Hash,
-                                              int64_t* localFileHeaderOffset, int64_t* dataSize);
-    // Calculates Local File Entry size including header using offset and data size from CDr.
-    // 0 indicates invalid Local File Entry.
-    size_t CalculateLocalFileEntrySize(int64_t localFileHeaderOffset, int64_t dataSize) const;
-
-  private:
-    std::string ReadMetadata(Location loc) const;
-
-    // Retrieve the location of the Central Directory Record.
-    Location GetCDLocation();
-
-    // Retrieve the location of the signature block starting from Central
-    // Directory Record
-    Location GetSignatureLocation(off_t cdRecordOffset);
-
-    // Find the End of Central Directory Record, starting from the end of the
-    // file.
-    off_t FindEndOfCDRecord() const;
-
-    // Find Central Directory Record, starting from the end of the file.
-    Location FindCDRecord(const char* cursor);
-
-    // Checks if the archive can be used.
-    bool ready() const { return fd_ >= 0; }
-
-    std::string path_;
-    off_t size_;
-    unique_fd fd_;
-};
diff --git a/adb/fastdeploy/deploypatchgenerator/apk_archive_test.cpp b/adb/fastdeploy/deploypatchgenerator/apk_archive_test.cpp
deleted file mode 100644
index 554cb57..0000000
--- a/adb/fastdeploy/deploypatchgenerator/apk_archive_test.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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 <iostream>
-
-#include <gtest/gtest.h>
-
-#include "apk_archive.h"
-
-// Friend test to get around private scope of ApkArchive private functions.
-class ApkArchiveTester {
-  public:
-    ApkArchiveTester(const std::string& path) : archive_(path) {}
-
-    bool ready() { return archive_.ready(); }
-
-    auto ExtractMetadata() { return archive_.ExtractMetadata(); }
-
-    ApkArchive::Location GetCDLocation() { return archive_.GetCDLocation(); }
-    ApkArchive::Location GetSignatureLocation(size_t start) {
-        return archive_.GetSignatureLocation(start);
-    }
-
-  private:
-    ApkArchive archive_;
-};
-
-TEST(ApkArchiveTest, TestApkArchiveSizes) {
-    ApkArchiveTester archiveTester("fastdeploy/testdata/sample.apk");
-    EXPECT_TRUE(archiveTester.ready());
-
-    ApkArchive::Location cdLoc = archiveTester.GetCDLocation();
-    EXPECT_TRUE(cdLoc.valid);
-    ASSERT_EQ(cdLoc.offset, 2044145u);
-    ASSERT_EQ(cdLoc.size, 49390u);
-
-    // Check that block can be retrieved
-    ApkArchive::Location sigLoc = archiveTester.GetSignatureLocation(cdLoc.offset);
-    EXPECT_TRUE(sigLoc.valid);
-    ASSERT_EQ(sigLoc.offset, 2040049u);
-    ASSERT_EQ(sigLoc.size, 4088u);
-}
-
-TEST(ApkArchiveTest, TestApkArchiveDump) {
-    ApkArchiveTester archiveTester("fastdeploy/testdata/sample.apk");
-    EXPECT_TRUE(archiveTester.ready());
-
-    auto dump = archiveTester.ExtractMetadata();
-    ASSERT_EQ(dump.cd().size(), 49390u);
-    ASSERT_EQ(dump.signature().size(), 4088u);
-}
-
-TEST(ApkArchiveTest, WrongApk) {
-    ApkArchiveTester archiveTester("fastdeploy/testdata/sample.cd");
-    EXPECT_TRUE(archiveTester.ready());
-
-    auto dump = archiveTester.ExtractMetadata();
-    ASSERT_EQ(dump.cd().size(), 0u);
-    ASSERT_EQ(dump.signature().size(), 0u);
-}
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
deleted file mode 100644
index 8aa7da7..0000000
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * 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 "deploy_patch_generator.h"
-
-#include <inttypes.h>
-#include <stdio.h>
-
-#include <algorithm>
-#include <fstream>
-#include <functional>
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <unordered_map>
-
-#include <openssl/md5.h>
-
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "android-base/file.h"
-#include "patch_utils.h"
-#include "sysdeps.h"
-
-using namespace com::android::fastdeploy;
-
-void DeployPatchGenerator::Log(const char* fmt, ...) {
-    va_list ap;
-    va_start(ap, fmt);
-    vprintf(fmt, ap);
-    printf("\n");
-    va_end(ap);
-}
-
-static std::string HexEncode(const void* in_buffer, unsigned int size) {
-    static const char kHexChars[] = "0123456789ABCDEF";
-
-    // Each input byte creates two output hex characters.
-    std::string out_buffer(size * 2, '\0');
-
-    for (unsigned int i = 0; i < size; ++i) {
-        char byte = ((const uint8_t*)in_buffer)[i];
-        out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf];
-        out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf];
-    }
-    return out_buffer;
-}
-
-void DeployPatchGenerator::APKEntryToLog(const APKEntry& entry) {
-    if (!is_verbose_) {
-        return;
-    }
-    Log("MD5: %s", HexEncode(entry.md5().data(), entry.md5().size()).c_str());
-    Log("Data Offset: %" PRId64, entry.dataoffset());
-    Log("Data Size: %" PRId64, entry.datasize());
-}
-
-void DeployPatchGenerator::APKMetaDataToLog(const APKMetaData& metadata) {
-    if (!is_verbose_) {
-        return;
-    }
-    Log("APK Metadata: %s", metadata.absolute_path().c_str());
-    for (int i = 0; i < metadata.entries_size(); i++) {
-        const APKEntry& entry = metadata.entries(i);
-        APKEntryToLog(entry);
-    }
-}
-
-void DeployPatchGenerator::ReportSavings(const std::vector<SimpleEntry>& identicalEntries,
-                                         uint64_t totalSize) {
-    uint64_t totalEqualBytes = 0;
-    uint64_t totalEqualFiles = 0;
-    for (size_t i = 0; i < identicalEntries.size(); i++) {
-        if (identicalEntries[i].deviceEntry != nullptr) {
-            totalEqualBytes += identicalEntries[i].localEntry->datasize();
-            totalEqualFiles++;
-        }
-    }
-    double savingPercent = (totalEqualBytes * 100.0f) / totalSize;
-    fprintf(stderr, "Detected %" PRIu64 " equal APK entries\n", totalEqualFiles);
-    fprintf(stderr, "%" PRIu64 " bytes are equal out of %" PRIu64 " (%.2f%%)\n", totalEqualBytes,
-            totalSize, savingPercent);
-}
-
-struct PatchEntry {
-    int64_t deltaFromDeviceDataStart = 0;
-    int64_t deviceDataOffset = 0;
-    int64_t deviceDataLength = 0;
-};
-static void WritePatchEntry(const PatchEntry& patchEntry, borrowed_fd input, borrowed_fd output,
-                            size_t* realSizeOut) {
-    if (!(patchEntry.deltaFromDeviceDataStart | patchEntry.deviceDataOffset |
-          patchEntry.deviceDataLength)) {
-        return;
-    }
-
-    PatchUtils::WriteLong(patchEntry.deltaFromDeviceDataStart, output);
-    if (patchEntry.deltaFromDeviceDataStart > 0) {
-        PatchUtils::Pipe(input, output, patchEntry.deltaFromDeviceDataStart);
-    }
-    auto hostDataLength = patchEntry.deviceDataLength;
-    adb_lseek(input, hostDataLength, SEEK_CUR);
-
-    PatchUtils::WriteLong(patchEntry.deviceDataOffset, output);
-    PatchUtils::WriteLong(patchEntry.deviceDataLength, output);
-
-    *realSizeOut += patchEntry.deltaFromDeviceDataStart + hostDataLength;
-}
-
-void DeployPatchGenerator::GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
-                                         const std::string& localApkPath,
-                                         const std::string& deviceApkPath, borrowed_fd output) {
-    unique_fd input(adb_open(localApkPath.c_str(), O_RDONLY | O_CLOEXEC));
-    size_t newApkSize = adb_lseek(input, 0L, SEEK_END);
-    adb_lseek(input, 0L, SEEK_SET);
-
-    // Header.
-    PatchUtils::WriteSignature(output);
-    PatchUtils::WriteLong(newApkSize, output);
-    PatchUtils::WriteString(deviceApkPath, output);
-
-    size_t currentSizeOut = 0;
-    size_t realSizeOut = 0;
-    // Write data from the host upto the first entry we have that matches a device entry. Then write
-    // the metadata about the device entry and repeat for all entries that match on device. Finally
-    // write out any data left. If the device and host APKs are exactly the same this ends up
-    // writing out zip metadata from the local APK followed by offsets to the data to use from the
-    // device APK.
-    PatchEntry patchEntry;
-    for (size_t i = 0, size = entriesToUseOnDevice.size(); i < size; ++i) {
-        auto&& entry = entriesToUseOnDevice[i];
-        int64_t hostDataOffset = entry.localEntry->dataoffset();
-        int64_t hostDataLength = entry.localEntry->datasize();
-        int64_t deviceDataOffset = entry.deviceEntry->dataoffset();
-        // Both entries are the same, using host data length.
-        int64_t deviceDataLength = hostDataLength;
-
-        int64_t deltaFromDeviceDataStart = hostDataOffset - currentSizeOut;
-        if (deltaFromDeviceDataStart > 0) {
-            WritePatchEntry(patchEntry, input, output, &realSizeOut);
-            patchEntry.deltaFromDeviceDataStart = deltaFromDeviceDataStart;
-            patchEntry.deviceDataOffset = deviceDataOffset;
-            patchEntry.deviceDataLength = deviceDataLength;
-        } else {
-            patchEntry.deviceDataLength += deviceDataLength;
-        }
-
-        currentSizeOut += deltaFromDeviceDataStart + hostDataLength;
-    }
-    WritePatchEntry(patchEntry, input, output, &realSizeOut);
-    if (realSizeOut != currentSizeOut) {
-        fprintf(stderr, "Size mismatch current %lld vs real %lld\n",
-                static_cast<long long>(currentSizeOut), static_cast<long long>(realSizeOut));
-        error_exit("Aborting");
-    }
-
-    if (newApkSize > currentSizeOut) {
-        PatchUtils::WriteLong(newApkSize - currentSizeOut, output);
-        PatchUtils::Pipe(input, output, newApkSize - currentSizeOut);
-        PatchUtils::WriteLong(0, output);
-        PatchUtils::WriteLong(0, output);
-    }
-}
-
-bool DeployPatchGenerator::CreatePatch(const char* localApkPath, APKMetaData deviceApkMetadata,
-                                       android::base::borrowed_fd output) {
-    return CreatePatch(PatchUtils::GetHostAPKMetaData(localApkPath), std::move(deviceApkMetadata),
-                       output);
-}
-
-bool DeployPatchGenerator::CreatePatch(APKMetaData localApkMetadata, APKMetaData deviceApkMetadata,
-                                       borrowed_fd output) {
-    // Log metadata info.
-    APKMetaDataToLog(deviceApkMetadata);
-    APKMetaDataToLog(localApkMetadata);
-
-    const std::string localApkPath = localApkMetadata.absolute_path();
-    const std::string deviceApkPath = deviceApkMetadata.absolute_path();
-
-    std::vector<SimpleEntry> identicalEntries;
-    uint64_t totalSize =
-            BuildIdenticalEntries(identicalEntries, localApkMetadata, deviceApkMetadata);
-    ReportSavings(identicalEntries, totalSize);
-    GeneratePatch(identicalEntries, localApkPath, deviceApkPath, output);
-
-    return true;
-}
-
-uint64_t DeployPatchGenerator::BuildIdenticalEntries(std::vector<SimpleEntry>& outIdenticalEntries,
-                                                     const APKMetaData& localApkMetadata,
-                                                     const APKMetaData& deviceApkMetadata) {
-    outIdenticalEntries.reserve(
-            std::min(localApkMetadata.entries_size(), deviceApkMetadata.entries_size()));
-
-    using md5Digest = std::pair<uint64_t, uint64_t>;
-    struct md5Hash {
-        size_t operator()(const md5Digest& digest) const {
-            std::hash<uint64_t> hasher;
-            size_t seed = 0;
-            seed ^= hasher(digest.first) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
-            seed ^= hasher(digest.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
-            return seed;
-        }
-    };
-    static_assert(sizeof(md5Digest) == MD5_DIGEST_LENGTH);
-    std::unordered_map<md5Digest, std::vector<const APKEntry*>, md5Hash> deviceEntries;
-    for (const auto& deviceEntry : deviceApkMetadata.entries()) {
-        md5Digest md5;
-        memcpy(&md5, deviceEntry.md5().data(), deviceEntry.md5().size());
-
-        deviceEntries[md5].push_back(&deviceEntry);
-    }
-
-    uint64_t totalSize = 0;
-    for (const auto& localEntry : localApkMetadata.entries()) {
-        totalSize += localEntry.datasize();
-
-        md5Digest md5;
-        memcpy(&md5, localEntry.md5().data(), localEntry.md5().size());
-
-        auto deviceEntriesIt = deviceEntries.find(md5);
-        if (deviceEntriesIt == deviceEntries.end()) {
-            continue;
-        }
-
-        for (const auto* deviceEntry : deviceEntriesIt->second) {
-            if (deviceEntry->md5() == localEntry.md5()) {
-                SimpleEntry simpleEntry;
-                simpleEntry.localEntry = &localEntry;
-                simpleEntry.deviceEntry = deviceEntry;
-                APKEntryToLog(localEntry);
-                outIdenticalEntries.push_back(simpleEntry);
-                break;
-            }
-        }
-    }
-    std::sort(outIdenticalEntries.begin(), outIdenticalEntries.end(),
-              [](const SimpleEntry& lhs, const SimpleEntry& rhs) {
-                  return lhs.localEntry->dataoffset() < rhs.localEntry->dataoffset();
-              });
-    return totalSize;
-}
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
deleted file mode 100644
index fd7eaee..0000000
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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 "adb_unique_fd.h"
-#include "fastdeploy/proto/ApkEntry.pb.h"
-
-/**
- * This class is responsible for creating a patch that can be accepted by the deployagent. The
- * patch format is documented in GeneratePatch.
- */
-class DeployPatchGenerator {
-  public:
-    using APKEntry = com::android::fastdeploy::APKEntry;
-    using APKMetaData = com::android::fastdeploy::APKMetaData;
-
-    /**
-     * Simple struct to hold mapping between local metadata and device metadata.
-     */
-    struct SimpleEntry {
-        const APKEntry* localEntry;
-        const APKEntry* deviceEntry;
-    };
-
-    /**
-     * If |is_verbose| is true ApkEntries that are similar between device and host are written to
-     * the console.
-     */
-    explicit DeployPatchGenerator(bool is_verbose) : is_verbose_(is_verbose) {}
-    /**
-     * Given a |localApkPath|, and the |deviceApkMetadata| from an installed APK this function
-     * writes a patch to the given |output|.
-     */
-    bool CreatePatch(const char* localApkPath, APKMetaData deviceApkMetadata,
-                     android::base::borrowed_fd output);
-
-  private:
-    bool is_verbose_;
-
-    /**
-     * Log function only logs data to stdout when |is_verbose_| is true.
-     */
-    void Log(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
-
-    /**
-     * Helper function to log the APKMetaData structure. If |is_verbose_| is false this function
-     * early outs. This function is used for debugging / information.
-     */
-    void APKMetaDataToLog(const APKMetaData& metadata);
-    /**
-     * Helper function to log APKEntry.
-     */
-    void APKEntryToLog(const APKEntry& entry);
-
-    /**
-     * Given the |localApkMetadata| metadata, and the |deviceApkMetadata| from an installed APK this
-     * function writes a patch to the given |output|.
-     */
-    bool CreatePatch(APKMetaData localApkMetadata, APKMetaData deviceApkMetadata,
-                     android::base::borrowed_fd output);
-
-    /**
-     * Helper function to report savings by fastdeploy. This function prints out savings even with
-     * |is_verbose_| set to false. |totalSize| is used to show a percentage of savings. Note:
-     * |totalSize| is the size of the ZipEntries. Not the size of the entire file. The metadata of
-     * the zip data needs to be sent across with every iteration.
-     * [Patch format]
-     * |Fixed String| Signature
-     * |long|         New Size of Apk
-     * |Packets[]|    Array of Packets
-     *
-     * [Packet Format]
-     * |long|     Size of data to use from patch
-     * |byte[]|   Patch data
-     * |long|     Offset of data to use already on device
-     * |long|     Length of data to read from device APK
-     * TODO(b/138306784): Move the patch format to a proto.
-     */
-    void ReportSavings(const std::vector<SimpleEntry>& identicalEntries, uint64_t totalSize);
-
-    /**
-     * This enumerates each entry in |entriesToUseOnDevice| and builds a patch file copying data
-     * from |localApkPath| where we are unable to use entries already on the device. The new patch
-     * is written to |output|. The entries are expected to be sorted by data offset from lowest to
-     * highest.
-     */
-    void GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
-                       const std::string& localApkPath, const std::string& deviceApkPath,
-                       android::base::borrowed_fd output);
-
-  protected:
-    uint64_t BuildIdenticalEntries(std::vector<SimpleEntry>& outIdenticalEntries,
-                                   const APKMetaData& localApkMetadata,
-                                   const APKMetaData& deviceApkMetadata);
-};
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
deleted file mode 100644
index e4c96ea..0000000
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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 "deploy_patch_generator.h"
-#include "apk_archive.h"
-#include "patch_utils.h"
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-#include <stdlib.h>
-#include <string.h>
-#include <string>
-
-#include "sysdeps.h"
-
-using namespace com::android::fastdeploy;
-
-static std::string GetTestFile(const std::string& name) {
-    return "fastdeploy/testdata/" + name;
-}
-
-struct TestPatchGenerator : DeployPatchGenerator {
-    using DeployPatchGenerator::BuildIdenticalEntries;
-    using DeployPatchGenerator::DeployPatchGenerator;
-};
-
-TEST(DeployPatchGeneratorTest, IdenticalFileEntries) {
-    std::string apkPath = GetTestFile("rotating_cube-release.apk");
-    APKMetaData metadataA = PatchUtils::GetHostAPKMetaData(apkPath.c_str());
-    TestPatchGenerator generator(false);
-    std::vector<DeployPatchGenerator::SimpleEntry> entries;
-    generator.BuildIdenticalEntries(entries, metadataA, metadataA);
-    // Expect the entry count to match the number of entries in the metadata.
-    const uint32_t identicalCount = entries.size();
-    const uint32_t entriesCount = metadataA.entries_size();
-    EXPECT_EQ(identicalCount, entriesCount);
-}
-
-TEST(DeployPatchGeneratorTest, NoDeviceMetadata) {
-    std::string apkPath = GetTestFile("rotating_cube-release.apk");
-    // Get size of our test apk.
-    long apkSize = 0;
-    {
-        unique_fd apkFile(adb_open(apkPath.c_str(), O_RDWR));
-        apkSize = adb_lseek(apkFile, 0L, SEEK_END);
-    }
-
-    // Create a patch that is 100% different.
-    TemporaryFile output;
-    DeployPatchGenerator generator(true);
-    generator.CreatePatch(apkPath.c_str(), {}, output.fd);
-
-    // Expect a patch file that has a size at least the size of our initial APK.
-    long patchSize = adb_lseek(output.fd, 0L, SEEK_END);
-    EXPECT_GT(patchSize, apkSize);
-}
-
-TEST(DeployPatchGeneratorTest, ZeroSizePatch) {
-    std::string apkPath = GetTestFile("rotating_cube-release.apk");
-    ApkArchive archive(apkPath);
-    auto dump = archive.ExtractMetadata();
-    EXPECT_NE(dump.cd().size(), 0u);
-
-    APKMetaData metadata = PatchUtils::GetDeviceAPKMetaData(dump);
-
-    // Create a patch that is 100% the same.
-    TemporaryFile output;
-    output.DoNotRemove();
-    DeployPatchGenerator generator(true);
-    generator.CreatePatch(apkPath.c_str(), metadata, output.fd);
-
-    // Expect a patch file that is smaller than 0.5K.
-    int64_t patchSize = adb_lseek(output.fd, 0L, SEEK_END);
-    EXPECT_LE(patchSize, 512);
-}
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/patch_utils.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
deleted file mode 100644
index 2b00c80..0000000
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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 "patch_utils.h"
-
-#include <stdio.h>
-
-#include "adb_io.h"
-#include "adb_utils.h"
-#include "android-base/endian.h"
-#include "sysdeps.h"
-
-#include "apk_archive.h"
-
-using namespace com::android;
-using namespace com::android::fastdeploy;
-using namespace android::base;
-
-static constexpr char kSignature[] = "FASTDEPLOY";
-
-APKMetaData PatchUtils::GetDeviceAPKMetaData(const APKDump& apk_dump) {
-    APKMetaData apkMetaData;
-    apkMetaData.set_absolute_path(apk_dump.absolute_path());
-
-    std::string md5Hash;
-    int64_t localFileHeaderOffset;
-    int64_t dataSize;
-
-    const auto& cd = apk_dump.cd();
-    auto cur = cd.data();
-    int64_t size = cd.size();
-    while (auto consumed = ApkArchive::ParseCentralDirectoryRecord(
-                   cur, size, &md5Hash, &localFileHeaderOffset, &dataSize)) {
-        cur += consumed;
-        size -= consumed;
-
-        auto apkEntry = apkMetaData.add_entries();
-        apkEntry->set_md5(md5Hash);
-        apkEntry->set_dataoffset(localFileHeaderOffset);
-        apkEntry->set_datasize(dataSize);
-    }
-    return apkMetaData;
-}
-
-APKMetaData PatchUtils::GetHostAPKMetaData(const char* apkPath) {
-    ApkArchive archive(apkPath);
-    auto dump = archive.ExtractMetadata();
-    if (dump.cd().empty()) {
-        fprintf(stderr, "adb: Could not extract Central Directory from %s\n", apkPath);
-        error_exit("Aborting");
-    }
-
-    auto apkMetaData = GetDeviceAPKMetaData(dump);
-
-    // Now let's set data sizes.
-    for (auto& apkEntry : *apkMetaData.mutable_entries()) {
-        auto dataSize =
-                archive.CalculateLocalFileEntrySize(apkEntry.dataoffset(), apkEntry.datasize());
-        if (dataSize == 0) {
-            error_exit("Aborting");
-        }
-        apkEntry.set_datasize(dataSize);
-    }
-
-    return apkMetaData;
-}
-
-void PatchUtils::WriteSignature(borrowed_fd output) {
-    WriteFdExactly(output, kSignature, sizeof(kSignature) - 1);
-}
-
-void PatchUtils::WriteLong(int64_t value, borrowed_fd output) {
-    int64_t littleEndian = htole64(value);
-    WriteFdExactly(output, &littleEndian, sizeof(littleEndian));
-}
-
-void PatchUtils::WriteString(const std::string& value, android::base::borrowed_fd output) {
-    WriteLong(value.size(), output);
-    WriteFdExactly(output, value);
-}
-
-void PatchUtils::Pipe(borrowed_fd input, borrowed_fd output, size_t amount) {
-    constexpr static size_t BUFFER_SIZE = 128 * 1024;
-    char buffer[BUFFER_SIZE];
-    size_t transferAmount = 0;
-    while (transferAmount != amount) {
-        auto chunkAmount = std::min(amount - transferAmount, BUFFER_SIZE);
-        auto readAmount = adb_read(input, buffer, chunkAmount);
-        if (readAmount < 0) {
-            fprintf(stderr, "adb: failed to read from input: %s\n", strerror(errno));
-            error_exit("Aborting");
-        }
-        WriteFdExactly(output, buffer, readAmount);
-        transferAmount += readAmount;
-    }
-}
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.h b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
deleted file mode 100644
index 8dc9b9c..0000000
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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"
-#include "fastdeploy/proto/ApkEntry.pb.h"
-
-/**
- * Helper class that mirrors the PatchUtils from deploy agent.
- */
-class PatchUtils {
-  public:
-    /**
-     * This function takes the dump of Central Directly and builds the APKMetaData required by the
-     * patching algorithm. The if this function has an error a string is printed to the terminal and
-     * exit(1) is called.
-     */
-    static com::android::fastdeploy::APKMetaData GetDeviceAPKMetaData(
-            const com::android::fastdeploy::APKDump& apk_dump);
-    /**
-     * This function takes a local APK file and builds the APKMetaData required by the patching
-     * algorithm. The if this function has an error a string is printed to the terminal and exit(1)
-     * is called.
-     */
-    static com::android::fastdeploy::APKMetaData GetHostAPKMetaData(const char* file);
-    /**
-     * Writes a fixed signature string to the header of the patch.
-     */
-    static void WriteSignature(android::base::borrowed_fd output);
-    /**
-     * Writes an int64 to the |output| reversing the bytes.
-     */
-    static void WriteLong(int64_t value, android::base::borrowed_fd output);
-    /**
-     * Writes string to the |output|.
-     */
-    static void WriteString(const std::string& value, android::base::borrowed_fd output);
-    /**
-     * Copy |amount| of data from |input| to |output|.
-     */
-    static void Pipe(android::base::borrowed_fd input, android::base::borrowed_fd output,
-                     size_t amount);
-};
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
deleted file mode 100644
index 3ec5ab3..0000000
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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 "patch_utils.h"
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sstream>
-#include <string>
-
-#include <google/protobuf/util/message_differencer.h>
-
-#include "adb_io.h"
-#include "sysdeps.h"
-
-using namespace com::android::fastdeploy;
-using google::protobuf::util::MessageDifferencer;
-
-static std::string GetTestFile(const std::string& name) {
-    return "fastdeploy/testdata/" + name;
-}
-
-bool FileMatchesContent(android::base::borrowed_fd input, const char* contents,
-                        ssize_t contentsSize) {
-    adb_lseek(input, 0, SEEK_SET);
-    // Use a temp buffer larger than any test contents.
-    constexpr int BUFFER_SIZE = 2048;
-    char buffer[BUFFER_SIZE];
-    bool result = true;
-    // Validate size of files is equal.
-    ssize_t readAmount = adb_read(input, buffer, BUFFER_SIZE);
-    EXPECT_EQ(readAmount, contentsSize);
-    result = memcmp(buffer, contents, readAmount) == 0;
-    for (int i = 0; i < readAmount; i++) {
-        printf("%x", buffer[i]);
-    }
-    printf(" == ");
-    for (int i = 0; i < contentsSize; i++) {
-        printf("%x", contents[i]);
-    }
-    printf("\n");
-
-    return result;
-}
-
-TEST(PatchUtilsTest, SwapLongWrites) {
-    TemporaryFile output;
-    PatchUtils::WriteLong(0x0011223344556677, output.fd);
-    adb_lseek(output.fd, 0, SEEK_SET);
-    const char expected[] = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
-    EXPECT_TRUE(FileMatchesContent(output.fd, expected, 8));
-}
-
-TEST(PatchUtilsTest, PipeWritesAmountToOutput) {
-    std::string expected("Some Data");
-    TemporaryFile input;
-    TemporaryFile output;
-    // Populate input file.
-    WriteFdExactly(input.fd, expected);
-    adb_lseek(input.fd, 0, SEEK_SET);
-    // Open input file for read, and output file for write.
-    PatchUtils::Pipe(input.fd, output.fd, expected.size());
-    // Validate pipe worked
-    EXPECT_TRUE(FileMatchesContent(output.fd, expected.c_str(), expected.size()));
-}
-
-TEST(PatchUtilsTest, SignatureConstMatches) {
-    std::string apkFile = GetTestFile("rotating_cube-release.apk");
-    TemporaryFile output;
-    PatchUtils::WriteSignature(output.fd);
-    std::string contents("FASTDEPLOY");
-    EXPECT_TRUE(FileMatchesContent(output.fd, contents.c_str(), contents.size()));
-}
-
-TEST(PatchUtilsTest, GatherMetadata) {
-    std::string apkFile = GetTestFile("rotating_cube-release.apk");
-    APKMetaData actual = PatchUtils::GetHostAPKMetaData(apkFile.c_str());
-
-    std::string expectedMetadata;
-    android::base::ReadFileToString(GetTestFile("rotating_cube-metadata-release.data"),
-                                    &expectedMetadata);
-    APKMetaData expected;
-    EXPECT_TRUE(expected.ParseFromString(expectedMetadata));
-
-    // Test paths might vary.
-    expected.set_absolute_path(actual.absolute_path());
-
-    std::string actualMetadata;
-    actual.SerializeToString(&actualMetadata);
-
-    expected.SerializeToString(&expectedMetadata);
-
-    EXPECT_EQ(expectedMetadata, actualMetadata);
-}
-
-static inline void sanitize(APKMetaData& metadata) {
-    metadata.clear_absolute_path();
-    for (auto&& entry : *metadata.mutable_entries()) {
-        entry.clear_datasize();
-    }
-}
-
-TEST(PatchUtilsTest, GatherDumpMetadata) {
-    APKMetaData hostMetadata;
-    APKMetaData deviceMetadata;
-
-    hostMetadata = PatchUtils::GetHostAPKMetaData(GetTestFile("sample.apk").c_str());
-
-    {
-        std::string cd;
-        android::base::ReadFileToString(GetTestFile("sample.cd"), &cd);
-
-        APKDump dump;
-        dump.set_cd(std::move(cd));
-
-        deviceMetadata = PatchUtils::GetDeviceAPKMetaData(dump);
-    }
-
-    sanitize(hostMetadata);
-    sanitize(deviceMetadata);
-
-    std::string expectedMetadata;
-    hostMetadata.SerializeToString(&expectedMetadata);
-
-    std::string actualMetadata;
-    deviceMetadata.SerializeToString(&actualMetadata);
-
-    EXPECT_EQ(expectedMetadata, actualMetadata);
-}
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
index d84c5a5..9460d15 100644
--- a/adb/fastdeploy/proto/ApkEntry.proto
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -1,26 +1,18 @@
-syntax = "proto3";
+syntax = "proto2";
 
 package com.android.fastdeploy;
 
 option java_package = "com.android.fastdeploy";
-option java_outer_classname = "ApkEntryProto";
 option java_multiple_files = true;
-option optimize_for = LITE_RUNTIME;
-
-message APKDump {
-    string name = 1;
-    bytes cd = 2;
-    bytes signature = 3;
-    string absolute_path = 4;
-}
 
 message APKEntry {
-    bytes md5 = 1;
-    int64 dataOffset = 2;
-    int64 dataSize = 3;
+    required int64 crc32 = 1;
+    required string fileName = 2;
+    required int64 dataOffset = 3;
+    required int64 compressedSize = 4;
+    required int64 uncompressedSize = 5;
 }
 
 message APKMetaData {
-    string absolute_path = 1;
-    repeated APKEntry entries = 2;
+    repeated APKEntry entries = 1;
 }
diff --git a/adb/fastdeploy/testdata/helloworld5.apk b/adb/fastdeploy/testdata/helloworld5.apk
deleted file mode 100644
index 4a1539e..0000000
--- a/adb/fastdeploy/testdata/helloworld5.apk
+++ /dev/null
Binary files differ
diff --git a/adb/fastdeploy/testdata/helloworld7.apk b/adb/fastdeploy/testdata/helloworld7.apk
deleted file mode 100644
index 82c46df..0000000
--- a/adb/fastdeploy/testdata/helloworld7.apk
+++ /dev/null
Binary files differ
diff --git a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
deleted file mode 100644
index 52352ff..0000000
--- a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
+++ /dev/null
Binary files differ
diff --git a/adb/fastdeploy/testdata/rotating_cube-release.apk b/adb/fastdeploy/testdata/rotating_cube-release.apk
deleted file mode 100644
index d47e0ea..0000000
--- a/adb/fastdeploy/testdata/rotating_cube-release.apk
+++ /dev/null
Binary files differ
diff --git a/adb/fastdeploy/testdata/sample.apk b/adb/fastdeploy/testdata/sample.apk
deleted file mode 100644
index c316205..0000000
--- a/adb/fastdeploy/testdata/sample.apk
+++ /dev/null
Binary files differ
diff --git a/adb/fastdeploy/testdata/sample.cd b/adb/fastdeploy/testdata/sample.cd
deleted file mode 100644
index 5e5b4d4..0000000
--- a/adb/fastdeploy/testdata/sample.cd
+++ /dev/null
Binary files differ
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
new file mode 100644
index 0000000..32f9086
--- /dev/null
+++ b/adb/fdevent.cpp
@@ -0,0 +1,553 @@
+/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
+**
+** Copyright 2006, Brian Swetland <swetland@frotz.net>
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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 FDEVENT
+
+#include "sysdeps.h"
+#include "fdevent.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <deque>
+#include <functional>
+#include <list>
+#include <mutex>
+#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
+
+#define FDE_ACTIVE     0x0100
+#define FDE_PENDING    0x0200
+#define FDE_CREATED    0x0400
+
+struct PollNode {
+  fdevent* fde;
+  adb_pollfd pollfd;
+
+  explicit PollNode(fdevent* fde) : fde(fde) {
+      memset(&pollfd, 0, sizeof(pollfd));
+      pollfd.fd = fde->fd.get();
+
+#if defined(__linux__)
+      // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
+      // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
+      pollfd.events = POLLRDHUP;
+#endif
+  }
+};
+
+// All operations to fdevent should happen only in the main thread.
+// That's why we don't need a lock for fdevent.
+static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
+static auto& g_pending_list = *new std::list<fdevent*>();
+static std::atomic<bool> terminate_loop(false);
+static bool main_thread_valid;
+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, android::base::GetThreadId());
+    }
+}
+
+void set_main_thread() {
+    main_thread_valid = true;
+    main_thread_id = android::base::GetThreadId();
+}
+
+static std::string dump_fde(const fdevent* fde) {
+    std::string state;
+    if (fde->state & FDE_ACTIVE) {
+        state += "A";
+    }
+    if (fde->state & FDE_PENDING) {
+        state += "P";
+    }
+    if (fde->state & FDE_CREATED) {
+        state += "C";
+    }
+    if (fde->state & FDE_READ) {
+        state += "R";
+    }
+    if (fde->state & FDE_WRITE) {
+        state += "W";
+    }
+    if (fde->state & FDE_ERROR) {
+        state += "E";
+    }
+    return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+                                       state.c_str());
+}
+
+template <typename F>
+static fdevent* fdevent_create_impl(int fd, F func, void* arg) {
+    check_main_thread();
+    CHECK_GE(fd, 0);
+
+    fdevent* fde = new fdevent();
+    fde->id = fdevent_id++;
+    fde->state = FDE_ACTIVE;
+    fde->fd.reset(fd);
+    fde->func = func;
+    fde->arg = arg;
+    if (!set_file_block_mode(fd, false)) {
+        // Here is not proper to handle the error. If it fails here, some error is
+        // likely to be detected by poll(), then we can let the callback function
+        // to handle it.
+        LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
+    }
+    auto pair = g_poll_node_map.emplace(fde->fd.get(), PollNode(fde));
+    CHECK(pair.second) << "install existing fd " << fd;
+
+    fde->state |= FDE_CREATED;
+    return 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();
+    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(result.get());
+
+        if (fde->state & FDE_PENDING) {
+            g_pending_list.remove(fde);
+        }
+        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.get());
+    CHECK(it != g_poll_node_map.end());
+    PollNode& node = it->second;
+    if (events & FDE_READ) {
+        node.pollfd.events |= POLLIN;
+    } else {
+        node.pollfd.events &= ~POLLIN;
+    }
+
+    if (events & FDE_WRITE) {
+        node.pollfd.events |= POLLOUT;
+    } else {
+        node.pollfd.events &= ~POLLOUT;
+    }
+    fde->state = (fde->state & FDE_STATEMASK) | events;
+}
+
+void fdevent_set(fdevent* fde, unsigned events) {
+    check_main_thread();
+    events &= FDE_EVENTMASK;
+    if ((fde->state & FDE_EVENTMASK) == events) {
+        return;
+    }
+    CHECK(fde->state & FDE_ACTIVE);
+    fdevent_update(fde, events);
+    D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
+
+    if (fde->state & FDE_PENDING) {
+        // If we are pending, make sure we don't signal an event that is no longer wanted.
+        fde->events &= events;
+        if (fde->events == 0) {
+            g_pending_list.remove(fde);
+            fde->state &= ~FDE_PENDING;
+        }
+    }
+}
+
+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) {
+        std::string op;
+        if (pollfd.events & POLLIN) {
+            op += "R";
+        }
+        if (pollfd.events & POLLOUT) {
+            op += "W";
+        }
+        android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
+    }
+    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) {
+        pollfds.push_back(pair.second.pollfd);
+    }
+    CHECK_GT(pollfds.size(), 0u);
+    D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+
+    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);
+        }
+        unsigned events = 0;
+        if (pollfd.revents & POLLIN) {
+            events |= FDE_READ;
+        }
+        if (pollfd.revents & POLLOUT) {
+            events |= FDE_WRITE;
+        }
+        if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+            // We fake a read, as the rest of the code assumes that errors will
+            // be detected at that point.
+            events |= FDE_READ | FDE_ERROR;
+        }
+#if defined(__linux__)
+        if (pollfd.revents & POLLRDHUP) {
+            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) {
+            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);
+        }
+    }
+}
+
+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());
+    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) {
+    // We need to be careful around reentrancy here, since a function we call can queue up another
+    // function.
+    while (true) {
+        std::function<void()> fn;
+        {
+            std::lock_guard<std::mutex> lock(run_queue_mutex);
+            if (run_queue.empty()) {
+                break;
+            }
+            fn = run_queue.front();
+            run_queue.pop_front();
+        }
+        fn();
+    }
+}
+
+static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
+    CHECK_GE(fd, 0);
+    CHECK(ev & FDE_READ);
+
+    char buf[1024];
+
+    // Empty the fd.
+    if (adb_read(fd, buf, sizeof(buf)) == -1) {
+        PLOG(FATAL) << "failed to empty run queue notify fd";
+    }
+
+    // Mark that we need to flush, and then run it at the end of fdevent_loop.
+    run_needs_flush = true;
+}
+
+static void fdevent_run_setup() {
+    {
+        std::lock_guard<std::mutex> lock(run_queue_mutex);
+        CHECK(run_queue_notify_fd.get() == -1);
+        int s[2];
+        if (adb_socketpair(s) != 0) {
+            PLOG(FATAL) << "failed to create run queue notify socketpair";
+        }
+
+        if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
+            PLOG(FATAL) << "failed to make run queue notify socket nonblocking";
+        }
+
+        run_queue_notify_fd.reset(s[0]);
+        fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+        CHECK(fde != nullptr);
+        fdevent_add(fde, FDE_READ);
+    }
+
+    fdevent_run_flush();
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    run_queue.push_back(std::move(fn));
+
+    // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
+    // In that case, rely on the setup code to flush the queue without a notification being needed.
+    if (run_queue_notify_fd != -1) {
+        int rc = adb_write(run_queue_notify_fd.get(), "", 1);
+
+        // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
+        if (rc == 0) {
+            PLOG(FATAL) << "run queue notify fd was closed?";
+        } else if (rc == -1 && errno != EAGAIN) {
+            PLOG(FATAL) << "failed to write to run queue notify fd";
+        }
+    }
+}
+
+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;
+        }
+
+        D("--- --- waiting for events");
+
+        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;
+        }
+    }
+}
+
+void fdevent_terminate_loop() {
+    terminate_loop = true;
+}
+
+size_t fdevent_installed_count() {
+    return g_poll_node_map.size();
+}
+
+void fdevent_reset() {
+    g_poll_node_map.clear();
+    g_pending_list.clear();
+
+    std::lock_guard<std::mutex> lock(run_queue_mutex);
+    run_queue_notify_fd.reset();
+    run_queue.clear();
+
+    main_thread_valid = false;
+    terminate_loop = false;
+}
diff --git a/adb/fdevent.h b/adb/fdevent.h
new file mode 100644
index 0000000..42dbb9e
--- /dev/null
+++ b/adb/fdevent.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FDEVENT_H
+#define __FDEVENT_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <chrono>
+#include <functional>
+#include <optional>
+#include <variant>
+
+#include "adb_unique_fd.h"
+
+// 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 {
+    uint64_t id;
+
+    unique_fd fd;
+    int force_eof = 0;
+
+    uint16_t state = 0;
+    uint16_t events = 0;
+    std::optional<std::chrono::milliseconds> timeout;
+    std::chrono::steady_clock::time_point last_active;
+
+    std::variant<fd_func, fd_func2> func;
+    void* arg = nullptr;
+};
+
+// 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);
+
+// Deallocate an fdevent object that was created by fdevent_create.
+void fdevent_destroy(fdevent *fde);
+
+// fdevent_destroy, except releasing the file descriptor previously owned by the fdevent.
+unique_fd fdevent_release(fdevent* fde);
+
+// 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);
+
+// 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.
+void fdevent_loop();
+
+void check_main_thread();
+
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
+// The following functions are used only for tests.
+void fdevent_terminate_loop();
+size_t fdevent_installed_count();
+void fdevent_reset();
+void set_main_thread();
+
+#endif
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
deleted file mode 100644
index 562f587..0000000
--- a/adb/fdevent/fdevent.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright 2006, Brian Swetland <swetland@frotz.net>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 FDEVENT
-
-#include "sysdeps.h"
-
-#include <inttypes.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/threads.h>
-
-#include "adb_utils.h"
-#include "fdevent.h"
-#include "fdevent_epoll.h"
-#include "fdevent_poll.h"
-
-using namespace std::chrono_literals;
-using std::chrono::duration_cast;
-
-void invoke_fde(struct fdevent* fde, unsigned events) {
-    if (auto f = std::get_if<fd_func>(&fde->func)) {
-        (*f)(fde->fd.get(), events, fde->arg);
-    } else if (auto f = std::get_if<fd_func2>(&fde->func)) {
-        (*f)(fde, events, fde->arg);
-    } else {
-        __builtin_unreachable();
-    }
-}
-
-std::string dump_fde(const fdevent* fde) {
-    std::string state;
-    if (fde->state & FDE_READ) {
-        state += "R";
-    }
-    if (fde->state & FDE_WRITE) {
-        state += "W";
-    }
-    if (fde->state & FDE_ERROR) {
-        state += "E";
-    }
-    return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
-                                       state.c_str());
-}
-
-fdevent* fdevent_context::Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg) {
-    CheckMainThread();
-    CHECK_GE(fd.get(), 0);
-
-    int fd_num = fd.get();
-
-    fdevent* fde = new fdevent();
-    fde->id = fdevent_id_++;
-    fde->state = 0;
-    fde->fd = std::move(fd);
-    fde->func = func;
-    fde->arg = arg;
-    if (!set_file_block_mode(fde->fd, false)) {
-        // Here is not proper to handle the error. If it fails here, some error is
-        // likely to be detected by poll(), then we can let the callback function
-        // to handle it.
-        LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
-    }
-
-    auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fde);
-    CHECK(inserted);
-    UNUSED(it);
-
-    this->Register(fde);
-    return fde;
-}
-
-unique_fd fdevent_context::Destroy(fdevent* fde) {
-    CheckMainThread();
-    if (!fde) {
-        return {};
-    }
-
-    this->Unregister(fde);
-
-    auto erased = this->installed_fdevents_.erase(fde->fd.get());
-    CHECK_EQ(1UL, erased);
-
-    unique_fd result = std::move(fde->fd);
-    delete fde;
-    return result;
-}
-
-void fdevent_context::Add(fdevent* fde, unsigned events) {
-    CHECK(!(events & FDE_TIMEOUT));
-    Set(fde, fde->state | events);
-}
-
-void fdevent_context::Del(fdevent* fde, unsigned events) {
-    CHECK(!(events & FDE_TIMEOUT));
-    Set(fde, fde->state & ~events);
-}
-
-void fdevent_context::SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
-    CheckMainThread();
-    fde->timeout = timeout;
-    fde->last_active = std::chrono::steady_clock::now();
-}
-
-std::optional<std::chrono::milliseconds> fdevent_context::CalculatePollDuration() {
-    std::optional<std::chrono::milliseconds> result = std::nullopt;
-    auto now = std::chrono::steady_clock::now();
-    CheckMainThread();
-
-    for (const auto& [fd, fde] : this->installed_fdevents_) {
-        UNUSED(fd);
-        auto timeout_opt = fde->timeout;
-        if (timeout_opt) {
-            auto deadline = fde->last_active + *timeout_opt;
-            auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now);
-            if (time_left < 0ms) {
-                time_left = 0ms;
-            }
-
-            if (!result) {
-                result = time_left;
-            } else {
-                result = std::min(*result, time_left);
-            }
-        }
-    }
-
-    return result;
-}
-
-void fdevent_context::HandleEvents(const std::vector<fdevent_event>& events) {
-    for (const auto& event : events) {
-        invoke_fde(event.fde, event.events);
-    }
-    FlushRunQueue();
-}
-
-void fdevent_context::FlushRunQueue() {
-    // We need to be careful around reentrancy here, since a function we call can queue up another
-    // function.
-    while (true) {
-        std::function<void()> fn;
-        {
-            std::lock_guard<std::mutex> lock(this->run_queue_mutex_);
-            if (this->run_queue_.empty()) {
-                break;
-            }
-            fn = std::move(this->run_queue_.front());
-            this->run_queue_.pop_front();
-        }
-        fn();
-    }
-}
-
-void fdevent_context::CheckMainThread() {
-    if (main_thread_id_) {
-        CHECK_EQ(*main_thread_id_, android::base::GetThreadId());
-    }
-}
-
-void fdevent_context::Run(std::function<void()> fn) {
-    {
-        std::lock_guard<std::mutex> lock(run_queue_mutex_);
-        run_queue_.push_back(std::move(fn));
-    }
-
-    Interrupt();
-}
-
-void fdevent_context::TerminateLoop() {
-    terminate_loop_ = true;
-    Interrupt();
-}
-
-static std::unique_ptr<fdevent_context> fdevent_create_context() {
-#if defined(__linux__)
-    return std::make_unique<fdevent_context_epoll>();
-#else
-    return std::make_unique<fdevent_context_poll>();
-#endif
-}
-
-static auto& g_ambient_fdevent_context =
-        *new std::unique_ptr<fdevent_context>(fdevent_create_context());
-
-static fdevent_context* fdevent_get_ambient() {
-    return g_ambient_fdevent_context.get();
-}
-
-fdevent* fdevent_create(int fd, fd_func func, void* arg) {
-    unique_fd ufd(fd);
-    return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
-}
-
-fdevent* fdevent_create(int fd, fd_func2 func, void* arg) {
-    unique_fd ufd(fd);
-    return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
-}
-
-unique_fd fdevent_release(fdevent* fde) {
-    return fdevent_get_ambient()->Destroy(fde);
-}
-
-void fdevent_destroy(fdevent* fde) {
-    fdevent_get_ambient()->Destroy(fde);
-}
-
-void fdevent_set(fdevent* fde, unsigned events) {
-    fdevent_get_ambient()->Set(fde, events);
-}
-
-void fdevent_add(fdevent* fde, unsigned events) {
-    fdevent_get_ambient()->Add(fde, events);
-}
-
-void fdevent_del(fdevent* fde, unsigned events) {
-    fdevent_get_ambient()->Del(fde, events);
-}
-
-void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
-    fdevent_get_ambient()->SetTimeout(fde, timeout);
-}
-
-void fdevent_run_on_main_thread(std::function<void()> fn) {
-    fdevent_get_ambient()->Run(std::move(fn));
-}
-
-void fdevent_loop() {
-    fdevent_get_ambient()->Loop();
-}
-
-void check_main_thread() {
-    fdevent_get_ambient()->CheckMainThread();
-}
-
-void fdevent_terminate_loop() {
-    fdevent_get_ambient()->TerminateLoop();
-}
-
-size_t fdevent_installed_count() {
-    return fdevent_get_ambient()->InstalledCount();
-}
-
-void fdevent_reset() {
-    g_ambient_fdevent_context = fdevent_create_context();
-}
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
deleted file mode 100644
index 86814d7..0000000
--- a/adb/fdevent/fdevent.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __FDEVENT_H
-#define __FDEVENT_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <atomic>
-#include <chrono>
-#include <deque>
-#include <functional>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-#include <variant>
-
-#include <android-base/thread_annotations.h>
-
-#include "adb_unique_fd.h"
-
-// Events that may be observed
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-#define FDE_TIMEOUT 0x0008
-
-struct fdevent;
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata);
-
-void invoke_fde(struct fdevent* fde, unsigned events);
-std::string dump_fde(const fdevent* fde);
-
-struct fdevent_event {
-    fdevent* fde;
-    unsigned events;
-};
-
-struct fdevent_context {
-  public:
-    virtual ~fdevent_context() = default;
-
-    // Allocate and initialize a new fdevent object.
-    fdevent* Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg);
-
-    // Deallocate an fdevent object, returning the file descriptor that was owned by it.
-    // Note that this calls Set, which is a virtual method, so destructors that call this must be
-    // final.
-    unique_fd Destroy(fdevent* fde);
-
-  protected:
-    virtual void Register(fdevent*) {}
-    virtual void Unregister(fdevent*) {}
-
-  public:
-    // Change which events should cause notifications.
-    virtual void Set(fdevent* fde, unsigned events) = 0;
-    void Add(fdevent* fde, unsigned events);
-    void Del(fdevent* fde, unsigned events);
-
-    // 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 SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
-
-  protected:
-    std::optional<std::chrono::milliseconds> CalculatePollDuration();
-    void HandleEvents(const std::vector<fdevent_event>& events);
-
-  private:
-    // Run all pending functions enqueued via Run().
-    void FlushRunQueue() EXCLUDES(run_queue_mutex_);
-
-  public:
-    // Loop until TerminateLoop is called, handling events.
-    // Implementations should call FlushRunQueue on every iteration, and check the value of
-    // terminate_loop_ to determine whether to stop.
-    virtual void Loop() = 0;
-
-    // Assert that the caller is either running on the context's main thread, or that there is no
-    // active main thread.
-    void CheckMainThread();
-
-    // Queue an operation to be run on the main thread.
-    void Run(std::function<void()> fn);
-
-    // Test-only functionality:
-    void TerminateLoop();
-    virtual size_t InstalledCount() = 0;
-
-  protected:
-    // Interrupt the run loop.
-    virtual void Interrupt() = 0;
-
-    std::optional<uint64_t> main_thread_id_ = std::nullopt;
-    std::atomic<bool> terminate_loop_ = false;
-
-  protected:
-    std::unordered_map<int, fdevent*> installed_fdevents_;
-
-  private:
-    uint64_t fdevent_id_ = 0;
-    std::mutex run_queue_mutex_;
-    std::deque<std::function<void()>> run_queue_ GUARDED_BY(run_queue_mutex_);
-};
-
-struct fdevent {
-    uint64_t id;
-
-    unique_fd fd;
-    int force_eof = 0;
-
-    uint16_t state = 0;
-    std::optional<std::chrono::milliseconds> timeout;
-    std::chrono::steady_clock::time_point last_active;
-
-    std::variant<fd_func, fd_func2> func;
-    void* arg = nullptr;
-};
-
-// Backwards compatibility shims that forward to the global fdevent_context.
-fdevent* fdevent_create(int fd, fd_func func, void* arg);
-fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
-
-unique_fd fdevent_release(fdevent* fde);
-void fdevent_destroy(fdevent* fde);
-
-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, std::optional<std::chrono::milliseconds> timeout);
-void fdevent_loop();
-void check_main_thread();
-
-// Queue an operation to run on the main thread.
-void fdevent_run_on_main_thread(std::function<void()> fn);
-
-// The following functions are used only for tests.
-void fdevent_terminate_loop();
-size_t fdevent_installed_count();
-void fdevent_reset();
-
-#endif
diff --git a/adb/fdevent/fdevent_epoll.cpp b/adb/fdevent/fdevent_epoll.cpp
deleted file mode 100644
index e3d1674..0000000
--- a/adb/fdevent/fdevent_epoll.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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 "fdevent_epoll.h"
-
-#if defined(__linux__)
-
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-
-#include <android-base/logging.h>
-#include <android-base/threads.h>
-
-#include "adb_unique_fd.h"
-#include "fdevent.h"
-
-static void fdevent_interrupt(int fd, unsigned, void*) {
-    uint64_t buf;
-    ssize_t rc = TEMP_FAILURE_RETRY(adb_read(fd, &buf, sizeof(buf)));
-    if (rc == -1) {
-        PLOG(FATAL) << "failed to read from fdevent interrupt fd";
-    }
-}
-
-fdevent_context_epoll::fdevent_context_epoll() {
-    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
-
-    unique_fd interrupt_fd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
-    if (interrupt_fd == -1) {
-        PLOG(FATAL) << "failed to create fdevent interrupt eventfd";
-    }
-
-    unique_fd interrupt_fd_dup(fcntl(interrupt_fd.get(), F_DUPFD_CLOEXEC, 3));
-    if (interrupt_fd_dup == -1) {
-        PLOG(FATAL) << "failed to dup fdevent interrupt eventfd";
-    }
-
-    this->interrupt_fd_ = std::move(interrupt_fd_dup);
-    fdevent* fde = this->Create(std::move(interrupt_fd), fdevent_interrupt, nullptr);
-    CHECK(fde != nullptr);
-    this->Add(fde, FDE_READ);
-}
-
-fdevent_context_epoll::~fdevent_context_epoll() {
-    // Destroy calls virtual methods, but this class is final, so that's okay.
-    this->Destroy(this->interrupt_fde_);
-}
-
-static epoll_event calculate_epoll_event(fdevent* fde) {
-    epoll_event result;
-    result.events = 0;
-    if (fde->state & FDE_READ) {
-        result.events |= EPOLLIN;
-    }
-    if (fde->state & FDE_WRITE) {
-        result.events |= EPOLLOUT;
-    }
-    if (fde->state & FDE_ERROR) {
-        result.events |= EPOLLERR;
-    }
-    result.events |= EPOLLRDHUP;
-    result.data.ptr = fde;
-    return result;
-}
-
-void fdevent_context_epoll::Register(fdevent* fde) {
-    epoll_event ev = calculate_epoll_event(fde);
-    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fde->fd.get(), &ev) != 0) {
-        PLOG(FATAL) << "failed to register fd " << fde->fd.get() << " with epoll";
-    }
-}
-
-void fdevent_context_epoll::Unregister(fdevent* fde) {
-    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, fde->fd.get(), nullptr) != 0) {
-        PLOG(FATAL) << "failed to unregister fd " << fde->fd.get() << " with epoll";
-    }
-}
-
-void fdevent_context_epoll::Set(fdevent* fde, unsigned events) {
-    unsigned previous_state = fde->state;
-    fde->state = events;
-
-    // If the state is the same, or only differed by FDE_TIMEOUT, we don't need to modify epoll.
-    if ((previous_state & ~FDE_TIMEOUT) == (events & ~FDE_TIMEOUT)) {
-        return;
-    }
-
-    epoll_event ev = calculate_epoll_event(fde);
-    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, fde->fd.get(), &ev) != 0) {
-        PLOG(FATAL) << "failed to modify fd " << fde->fd.get() << " with epoll";
-    }
-}
-
-void fdevent_context_epoll::Loop() {
-    main_thread_id_ = android::base::GetThreadId();
-
-    std::vector<fdevent_event> fde_events;
-    std::vector<epoll_event> epoll_events;
-    epoll_events.resize(this->installed_fdevents_.size());
-
-    while (true) {
-        if (terminate_loop_) {
-            break;
-        }
-
-        int rc = -1;
-        while (rc == -1) {
-            std::optional<std::chrono::milliseconds> timeout = CalculatePollDuration();
-            int timeout_ms;
-            if (!timeout) {
-                timeout_ms = -1;
-            } else {
-                timeout_ms = timeout->count();
-            }
-
-            rc = epoll_wait(epoll_fd_.get(), epoll_events.data(), epoll_events.size(), timeout_ms);
-            if (rc == -1 && errno != EINTR) {
-                PLOG(FATAL) << "epoll_wait failed";
-            }
-        }
-
-        auto post_poll = std::chrono::steady_clock::now();
-        std::unordered_map<fdevent*, unsigned> event_map;
-        for (int i = 0; i < rc; ++i) {
-            fdevent* fde = static_cast<fdevent*>(epoll_events[i].data.ptr);
-
-            unsigned events = 0;
-            if (epoll_events[i].events & EPOLLIN) {
-                CHECK(fde->state & FDE_READ);
-                events |= FDE_READ;
-            }
-            if (epoll_events[i].events & EPOLLOUT) {
-                CHECK(fde->state & FDE_WRITE);
-                events |= FDE_WRITE;
-            }
-            if (epoll_events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
-                // We fake a read, as the rest of the code assumes that errors will
-                // be detected at that point.
-                events |= FDE_READ | FDE_ERROR;
-            }
-
-            event_map[fde] = events;
-        }
-
-        for (const auto& [fd, fde] : installed_fdevents_) {
-            unsigned events = 0;
-            if (auto it = event_map.find(fde); it != event_map.end()) {
-                events = it->second;
-            }
-
-            if (events == 0) {
-                if (fde->timeout) {
-                    auto deadline = fde->last_active + *fde->timeout;
-                    if (deadline < post_poll) {
-                        events |= FDE_TIMEOUT;
-                    }
-                }
-            }
-
-            if (events != 0) {
-                LOG(DEBUG) << dump_fde(fde) << " got events " << std::hex << std::showbase
-                           << events;
-                fde_events.push_back({fde, events});
-                fde->last_active = post_poll;
-            }
-        }
-        this->HandleEvents(std::move(fde_events));
-        fde_events.clear();
-    }
-
-    main_thread_id_.reset();
-}
-
-size_t fdevent_context_epoll::InstalledCount() {
-    // We always have an installed fde for interrupt.
-    return this->installed_fdevents_.size() - 1;
-}
-
-void fdevent_context_epoll::Interrupt() {
-    uint64_t i = 1;
-    ssize_t rc = TEMP_FAILURE_RETRY(adb_write(this->interrupt_fd_, &i, sizeof(i)));
-    if (rc != sizeof(i)) {
-        PLOG(FATAL) << "failed to write to fdevent interrupt eventfd";
-    }
-}
-
-#endif  // defined(__linux__)
diff --git a/adb/fdevent/fdevent_epoll.h b/adb/fdevent/fdevent_epoll.h
deleted file mode 100644
index 684fa32..0000000
--- a/adb/fdevent/fdevent_epoll.h
+++ /dev/null
@@ -1,61 +0,0 @@
-#pragma once
-
-/*
- * 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.
- */
-
-#if defined(__linux__)
-
-#include "sysdeps.h"
-
-#include <sys/epoll.h>
-
-#include <deque>
-#include <list>
-#include <mutex>
-#include <unordered_map>
-
-#include <android-base/thread_annotations.h>
-
-#include "adb_unique_fd.h"
-#include "fdevent.h"
-
-struct fdevent_context_epoll final : public fdevent_context {
-    fdevent_context_epoll();
-    virtual ~fdevent_context_epoll();
-
-    virtual void Register(fdevent* fde) final;
-    virtual void Unregister(fdevent* fde) final;
-
-    virtual void Set(fdevent* fde, unsigned events) final;
-
-    virtual void Loop() final;
-    size_t InstalledCount() final;
-
-  protected:
-    virtual void Interrupt() final;
-
-  public:
-    // All operations to fdevent should happen only in the main thread.
-    // That's why we don't need a lock for fdevent.
-    std::unordered_map<int, fdevent*> epoll_node_map_;
-    std::list<fdevent*> pending_list_;
-
-    unique_fd epoll_fd_;
-    unique_fd interrupt_fd_;
-    fdevent* interrupt_fde_ = nullptr;
-};
-
-#endif  // defined(__linux__)
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
deleted file mode 100644
index cc4a7a1..0000000
--- a/adb/fdevent/fdevent_poll.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * 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 FDEVENT
-
-#include "sysdeps.h"
-#include "fdevent_poll.h"
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <atomic>
-#include <deque>
-#include <functional>
-#include <list>
-#include <mutex>
-#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/threads.h>
-
-#include "adb_io.h"
-#include "adb_trace.h"
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "fdevent.h"
-#include "sysdeps/chrono.h"
-
-static void fdevent_interrupt(int fd, unsigned, void*) {
-    char buf[BUFSIZ];
-    ssize_t rc = TEMP_FAILURE_RETRY(adb_read(fd, buf, sizeof(buf)));
-    if (rc == -1) {
-        PLOG(FATAL) << "failed to read from fdevent interrupt fd";
-    }
-}
-
-fdevent_context_poll::fdevent_context_poll() {
-    int s[2];
-    if (adb_socketpair(s) != 0) {
-        PLOG(FATAL) << "failed to create fdevent interrupt socketpair";
-    }
-
-    if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
-        PLOG(FATAL) << "failed to make fdevent interrupt socket nonblocking";
-    }
-
-    this->interrupt_fd_.reset(s[0]);
-    fdevent* fde = this->Create(unique_fd(s[1]), fdevent_interrupt, nullptr);
-    CHECK(fde != nullptr);
-    this->Add(fde, FDE_READ);
-}
-
-fdevent_context_poll::~fdevent_context_poll() {
-    // Destroy calls virtual methods, but this class is final, so that's okay.
-    this->Destroy(this->interrupt_fde_);
-}
-
-void fdevent_context_poll::Set(fdevent* fde, unsigned events) {
-    CheckMainThread();
-    fde->state = events;
-    D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
-}
-
-static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
-    std::string result;
-    for (const auto& pollfd : pollfds) {
-        std::string op;
-        if (pollfd.events & POLLIN) {
-            op += "R";
-        }
-        if (pollfd.events & POLLOUT) {
-            op += "W";
-        }
-        android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
-    }
-    return result;
-}
-
-void fdevent_context_poll::Loop() {
-    main_thread_id_ = android::base::GetThreadId();
-
-    while (true) {
-        if (terminate_loop_) {
-            break;
-        }
-
-        D("--- --- waiting for events");
-        std::vector<adb_pollfd> pollfds;
-        for (const auto& [fd, fde] : this->installed_fdevents_) {
-            adb_pollfd pfd;
-            pfd.fd = fd;
-            pfd.events = 0;
-            if (fde->state & FDE_READ) {
-                pfd.events |= POLLIN;
-            }
-            if (fde->state & FDE_WRITE) {
-                pfd.events |= POLLOUT;
-            }
-            if (fde->state & FDE_ERROR) {
-                pfd.events |= POLLERR;
-            }
-#if defined(__linux__)
-            pfd.events |= POLLRDHUP;
-#endif
-            pfd.revents = 0;
-            pollfds.push_back(pfd);
-        }
-        CHECK_GT(pollfds.size(), 0u);
-        D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
-
-        std::optional<std::chrono::milliseconds> timeout = CalculatePollDuration();
-        int timeout_ms;
-        if (!timeout) {
-            timeout_ms = -1;
-        } else {
-            timeout_ms = timeout->count();
-        }
-
-        int ret = adb_poll(pollfds.data(), pollfds.size(), timeout_ms);
-        if (ret == -1) {
-            PLOG(ERROR) << "poll(), ret = " << ret;
-            return;
-        }
-
-        auto post_poll = std::chrono::steady_clock::now();
-        std::vector<fdevent_event> poll_events;
-
-        for (const auto& pollfd : pollfds) {
-            unsigned events = 0;
-            if (pollfd.revents & POLLIN) {
-                events |= FDE_READ;
-            }
-            if (pollfd.revents & POLLOUT) {
-                events |= FDE_WRITE;
-            }
-            if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
-                // We fake a read, as the rest of the code assumes that errors will
-                // be detected at that point.
-                events |= FDE_READ | FDE_ERROR;
-            }
-#if defined(__linux__)
-            if (pollfd.revents & POLLRDHUP) {
-                events |= FDE_READ | FDE_ERROR;
-            }
-#endif
-
-            auto it = this->installed_fdevents_.find(pollfd.fd);
-            CHECK(it != this->installed_fdevents_.end());
-            fdevent* fde = it->second;
-
-            if (events == 0) {
-                if (fde->timeout) {
-                    auto deadline = fde->last_active + *fde->timeout;
-                    if (deadline < post_poll) {
-                        events |= FDE_TIMEOUT;
-                    }
-                }
-            }
-
-            if (events != 0) {
-                D("%s got events %x", dump_fde(fde).c_str(), events);
-                poll_events.push_back({fde, events});
-                fde->last_active = post_poll;
-            }
-        }
-        this->HandleEvents(std::move(poll_events));
-    }
-
-    main_thread_id_.reset();
-}
-
-size_t fdevent_context_poll::InstalledCount() {
-    // We always have an installed fde for interrupt.
-    return this->installed_fdevents_.size() - 1;
-}
-
-void fdevent_context_poll::Interrupt() {
-    int rc = adb_write(this->interrupt_fd_, "", 1);
-
-    // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
-    if (rc == 0) {
-        PLOG(FATAL) << "fdevent interrupt fd was closed?";
-    } else if (rc == -1 && errno != EAGAIN) {
-        PLOG(FATAL) << "failed to write to fdevent interrupt fd";
-    }
-}
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
deleted file mode 100644
index 98abab2..0000000
--- a/adb/fdevent/fdevent_poll.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#pragma once
-
-/*
- * 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 "sysdeps.h"
-
-#include <deque>
-#include <list>
-#include <mutex>
-#include <unordered_map>
-
-#include <android-base/thread_annotations.h>
-
-#include "adb_unique_fd.h"
-#include "fdevent.h"
-
-struct PollNode {
-  fdevent* fde;
-  adb_pollfd pollfd;
-
-  explicit PollNode(fdevent* fde) : fde(fde) {
-      memset(&pollfd, 0, sizeof(pollfd));
-      pollfd.fd = fde->fd.get();
-
-#if defined(__linux__)
-      // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
-      // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
-      pollfd.events = POLLRDHUP;
-#endif
-  }
-};
-
-struct fdevent_context_poll final : public fdevent_context {
-    fdevent_context_poll();
-    virtual ~fdevent_context_poll();
-
-    virtual void Set(fdevent* fde, unsigned events) final;
-
-    virtual void Loop() final;
-
-    virtual size_t InstalledCount() final;
-
-  protected:
-    virtual void Interrupt() final;
-
-  public:
-    unique_fd interrupt_fd_;
-    fdevent* interrupt_fde_ = nullptr;
-};
diff --git a/adb/fdevent/fdevent_test.cpp b/adb/fdevent_test.cpp
similarity index 87%
rename from adb/fdevent/fdevent_test.cpp
rename to adb/fdevent_test.cpp
index e06b3b3..682f061 100644
--- a/adb/fdevent/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -118,8 +118,8 @@
 TEST_F(FdeventTest, smoke) {
     for (bool use_new_callback : {true, false}) {
         fdevent_reset();
-        const size_t PIPE_COUNT = 512;
-        const size_t MESSAGE_LOOP_COUNT = 10;
+        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];
@@ -172,6 +172,44 @@
     }
 }
 
+struct InvalidFdArg {
+    fdevent* fde;
+    unsigned expected_events;
+    size_t* happened_event_count;
+};
+
+static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
+    InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
+    ASSERT_EQ(arg->expected_events, events);
+    fdevent_destroy(arg->fde);
+    if (++*(arg->happened_event_count) == 2) {
+        fdevent_terminate_loop();
+    }
+}
+
+static void InvalidFdThreadFunc() {
+    const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
+    size_t happened_event_count = 0;
+    InvalidFdArg read_arg;
+    read_arg.expected_events = FDE_READ | FDE_ERROR;
+    read_arg.happened_event_count = &happened_event_count;
+    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;
+    write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
+    fdevent_add(write_arg.fde, FDE_WRITE);
+    fdevent_loop();
+}
+
+TEST_F(FdeventTest, invalid_fd) {
+    std::thread thread(InvalidFdThreadFunc);
+    thread.join();
+}
+
 TEST_F(FdeventTest, run_on_main_thread) {
     std::vector<int> vec;
 
diff --git a/adb/fdevent/fdevent_test.h b/adb/fdevent_test.h
similarity index 95%
rename from adb/fdevent/fdevent_test.h
rename to adb/fdevent_test.h
index 2139d0f..24bce59 100644
--- a/adb/fdevent/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -78,8 +78,8 @@
     }
 
     size_t GetAdditionalLocalSocketCount() {
-        // dummy socket installed in PrepareThread()
-        return 1;
+        // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
+        return 2;
     }
 
     void TerminateThread() {
diff --git a/adb/services.cpp b/adb/services.cpp
index 6185aa6..335ffc4 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -100,7 +100,7 @@
     ConnectionState state;
 };
 
-static void wait_for_state(unique_fd fd, state_info* sinfo) {
+static void wait_for_state(int fd, state_info* sinfo) {
     D("wait_for_state %d", sinfo->state);
 
     while (true) {
@@ -122,7 +122,7 @@
         }
 
         if (!is_ambiguous) {
-            adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN};
+            adb_pollfd pfd = {.fd = fd, .events = POLLIN};
             int rc = adb_poll(&pfd, 1, 100);
             if (rc < 0) {
                 SendFail(fd, error);
@@ -140,6 +140,7 @@
         }
     }
 
+    adb_close(fd);
     D("wait_for_state is done");
 }
 
@@ -202,7 +203,7 @@
         return create_device_tracker(false);
     } else if (name == "track-devices-l") {
         return create_device_tracker(true);
-    } else if (android::base::ConsumePrefix(&name, "wait-for-")) {
+    } else if (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));
@@ -212,11 +213,11 @@
         sinfo->serial = serial;
         sinfo->transport_id = transport_id;
 
-        if (android::base::ConsumePrefix(&name, "local")) {
+        if (ConsumePrefix(&name, "local")) {
             sinfo->transport_type = kTransportLocal;
-        } else if (android::base::ConsumePrefix(&name, "usb")) {
+        } else if (ConsumePrefix(&name, "usb")) {
             sinfo->transport_type = kTransportUsb;
-        } else if (android::base::ConsumePrefix(&name, "any")) {
+        } else if (ConsumePrefix(&name, "any")) {
             sinfo->transport_type = kTransportAny;
         } else {
             return nullptr;
@@ -240,10 +241,11 @@
             return nullptr;
         }
 
-        unique_fd fd = create_service_thread(
-                "wait", [sinfo](unique_fd fd) { wait_for_state(std::move(fd), sinfo.get()); });
+        unique_fd fd = create_service_thread("wait", [sinfo](int fd) {
+            wait_for_state(fd, sinfo.get());
+        });
         return create_local_socket(std::move(fd));
-    } else if (android::base::ConsumePrefix(&name, "connect:")) {
+    } else if (ConsumePrefix(&name, "connect:")) {
         std::string host(name);
         unique_fd fd = create_service_thread(
                 "connect", std::bind(connect_service, std::placeholders::_1, host));
diff --git a/adb/shell_protocol.h b/adb/shell_protocol.h
index 4aab813..2c82689 100644
--- a/adb/shell_protocol.h
+++ b/adb/shell_protocol.h
@@ -21,7 +21,6 @@
 #include <android-base/macros.h>
 
 #include "adb.h"
-#include "adb_unique_fd.h"
 
 // Class to send and receive shell protocol packets.
 //
@@ -61,7 +60,7 @@
     // 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);
+    explicit ShellProtocol(int fd);
     virtual ~ShellProtocol();
 
     // Returns a pointer to the data buffer.
@@ -104,7 +103,7 @@
         kHeaderSize = sizeof(Id) + sizeof(length_t)
     };
 
-    borrowed_fd fd_;
+    int fd_;
     char buffer_[kBufferSize];
     size_t data_length_ = 0, bytes_left_ = 0;
 
diff --git a/adb/shell_service_protocol.cpp b/adb/shell_service_protocol.cpp
index 95afaff..13b66ec 100644
--- a/adb/shell_service_protocol.cpp
+++ b/adb/shell_service_protocol.cpp
@@ -22,7 +22,7 @@
 
 #include "adb_io.h"
 
-ShellProtocol::ShellProtocol(borrowed_fd fd) : fd_(fd) {
+ShellProtocol::ShellProtocol(int fd) : fd_(fd) {
     buffer_[0] = kIdInvalid;
 }
 
diff --git a/adb/socket.h b/adb/socket.h
index 4276851..b8c559a 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -24,7 +24,7 @@
 #include <string>
 
 #include "adb_unique_fd.h"
-#include "fdevent/fdevent.h"
+#include "fdevent.h"
 #include "types.h"
 
 class atransport;
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 98468b5..de4fff9 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -93,7 +93,7 @@
         }
     } else {
         std::string addr(spec.substr(4));
-        port_value = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+        port_value = -1;
 
         // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
         //        on an address that isn't 'localhost' is unsupported.
@@ -314,14 +314,14 @@
         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)) {
+        if (bind(serverfd, reinterpret_cast<struct sockaddr*>(&addr), addr_len)) {
             return -1;
         }
-        if (listen(serverfd.get(), 4)) {
+        if (listen(serverfd, 4)) {
             return -1;
         }
         if (serverfd >= 0 && resolved_port) {
-            if (getsockname(serverfd.get(), reinterpret_cast<sockaddr*>(&addr), &addr_len) == 0) {
+            if (getsockname(serverfd, reinterpret_cast<sockaddr*>(&addr), &addr_len) == 0) {
                 *resolved_port = addr.svm_port;
             } else {
                 return -1;
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index 3a2f60c..f5ec0f1 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -20,71 +20,38 @@
 
 #include <gtest/gtest.h>
 
-TEST(socket_spec, parse_tcp_socket_spec_just_port) {
+TEST(socket_spec, parse_tcp_socket_spec) {
     std::string hostname, error, serial;
     int port;
     EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &serial, &error));
     EXPECT_EQ("", hostname);
     EXPECT_EQ(5037, port);
     EXPECT_EQ("", serial);
-}
 
-TEST(socket_spec, parse_tcp_socket_spec_bad_ports) {
-    std::string hostname, error, serial;
-    int port;
+    // Bad ports:
     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));
-}
 
-TEST(socket_spec, parse_tcp_socket_spec_host_and_port) {
-    std::string hostname, error, serial;
-    int port;
     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);
-}
 
-TEST(socket_spec, parse_tcp_socket_spec_host_no_port) {
-    std::string hostname, error, serial;
-    int port;
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
-    EXPECT_EQ("localhost", hostname);
-    EXPECT_EQ(5555, port);
-    EXPECT_EQ("localhost:5555", serial);
-}
-
-TEST(socket_spec, parse_tcp_socket_spec_host_bad_ports) {
-    std::string hostname, error, serial;
-    int port;
+    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));
-}
 
-TEST(socket_spec, parse_tcp_socket_spec_ipv6_and_port) {
-    std::string hostname, error, serial;
-    int port;
+    // IPv6:
     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);
-}
 
-TEST(socket_spec, parse_tcp_socket_spec_ipv6_no_port) {
-    std::string hostname, error, serial;
-    int port;
-    EXPECT_TRUE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
-    EXPECT_EQ("::1", hostname);
-    EXPECT_EQ(5555, port);
-    EXPECT_EQ("[::1]:5555", serial);
-}
-
-TEST(socket_spec, parse_tcp_socket_spec_ipv6_bad_ports) {
-    std::string hostname, error, serial;
-    int port;
     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 1601ff0..5e28f76 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "fdevent/fdevent.h"
+#include "fdevent.h"
 
 #include <gtest/gtest.h>
 
@@ -29,7 +29,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
-#include "fdevent/fdevent_test.h"
+#include "fdevent_test.h"
 #include "socket.h"
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e78530c..8a2bf9a 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -31,8 +31,6 @@
 #include <string>
 #include <vector>
 
-#include <android-base/strings.h>
-
 #if !ADB_HOST
 #include <android-base/properties.h>
 #include <log/log_properties.h>
@@ -757,28 +755,26 @@
 
 #if ADB_HOST
     service = std::string_view(s->smart_socket_data).substr(4);
-
-    // TODO: These should be handled in handle_host_request.
-    if (android::base::ConsumePrefix(&service, "host-serial:")) {
+    if (ConsumePrefix(&service, "host-serial:")) {
         // serial number should follow "host:" and could be a host:port string.
         if (!internal::parse_host_service(&serial, &service, service)) {
             LOG(ERROR) << "SS(" << s->id << "): failed to parse host service: " << service;
             goto fail;
         }
-    } else if (android::base::ConsumePrefix(&service, "host-transport-id:")) {
+    } else if (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, ":")) {
+        if (!ConsumePrefix(&service, ":")) {
             LOG(ERROR) << "SS(" << s->id << "): host-transport-id without command";
             return -1;
         }
-    } else if (android::base::ConsumePrefix(&service, "host-usb:")) {
+    } else if (ConsumePrefix(&service, "host-usb:")) {
         type = kTransportUsb;
-    } else if (android::base::ConsumePrefix(&service, "host-local:")) {
+    } else if (ConsumePrefix(&service, "host-local:")) {
         type = kTransportLocal;
-    } else if (android::base::ConsumePrefix(&service, "host:")) {
+    } else if (ConsumePrefix(&service, "host:")) {
         type = kTransportAny;
     } else {
         service = std::string_view{};
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 466c2ce..15247e7 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -33,11 +33,9 @@
 // Include this before open/close/unlink are defined as macros below.
 #include <android-base/errors.h>
 #include <android-base/macros.h>
-#include <android-base/off64_t.h>
 #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"
@@ -65,6 +63,8 @@
 #include <memory>   // unique_ptr
 #include <string>
 
+#include "fdevent.h"
+
 #define OS_PATH_SEPARATORS "\\/"
 #define OS_PATH_SEPARATOR '\\'
 #define OS_PATH_SEPARATOR_STR "\\"
@@ -76,71 +76,66 @@
 
 extern int adb_thread_setname(const std::string& name);
 
-static __inline__ void close_on_exec(borrowed_fd fd) {
+static __inline__ void  close_on_exec(int  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(borrowed_fd fd, void* buf, int len);
-extern int adb_pread(borrowed_fd fd, void* buf, int len, off64_t offset);
-extern int adb_write(borrowed_fd fd, const void* buf, int len);
-extern int adb_pwrite(borrowed_fd fd, const void* buf, int len, off64_t offset);
-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_read(int fd, void* buf, int len);
+extern int adb_write(int fd, const void* buf, int len);
+extern int64_t adb_lseek(int fd, int64_t pos, int where);
+extern int adb_shutdown(int fd, int direction = SHUT_RDWR);
 extern int adb_close(int fd);
 extern int adb_register_socket(SOCKET s);
-extern HANDLE adb_get_os_handle(borrowed_fd fd);
 
 // 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(borrowed_fd fd, void* buf, size_t len);
+extern int  unix_read_interruptible(int  fd, void*  buf, size_t  len);
 
 // See the comments for the !defined(_WIN32) version of unix_read().
-static __inline__ int unix_read(borrowed_fd fd, void* buf, size_t len) {
+static __inline__ int unix_read(int fd, void* buf, size_t len) {
     return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
 }
 
 #undef   read
 #define  read  ___xxx_read
 
-#undef pread
-#define pread ___xxx_pread
-
 // See the comments for the !defined(_WIN32) version of unix_write().
-static __inline__ int unix_write(borrowed_fd fd, const void* buf, size_t len) {
-    return write(fd.get(), buf, len);
+static __inline__  int  unix_write(int  fd, const void*  buf, size_t  len)
+{
+    return write(fd, buf, len);
 }
 #undef   write
 #define  write  ___xxx_write
 
-#undef pwrite
-#define pwrite ___xxx_pwrite
-
 // 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);
+static __inline__ int unix_lseek(int fd, int pos, int where) {
+    return lseek(fd, 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);
 }
 
@@ -157,7 +152,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(borrowed_fd fd);
+int unix_isatty(int fd);
 #define  isatty  ___xxx_isatty
 
 int network_inaddr_any_server(int port, int type, std::string* error);
@@ -173,21 +168,20 @@
 int network_connect(const std::string& host, int port, int type, int timeout,
                     std::string* error);
 
-extern int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen);
+extern int  adb_socket_accept(int  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(borrowed_fd fd);
+int adb_socket_get_local_port(int fd);
 
-extern int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval,
-                          socklen_t optlen);
+extern int  adb_setsockopt(int  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;
@@ -220,7 +214,8 @@
 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);
 
@@ -349,8 +344,9 @@
     return c == '/';
 }
 
-static __inline__ void close_on_exec(borrowed_fd fd) {
-    fcntl(fd.get(), F_SETFD, FD_CLOEXEC);
+static __inline__ void  close_on_exec(int  fd)
+{
+    fcntl( fd, F_SETFD, FD_CLOEXEC );
 }
 
 // Open a file and return a file descriptor that may be used with unix_read(),
@@ -378,10 +374,12 @@
 
 // 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().
 //
@@ -389,21 +387,23 @@
 // 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(borrowed_fd fd, int direction = SHUT_RDWR) {
-    return shutdown(fd.get(), direction);
+static __inline__ int adb_shutdown(int fd, int direction = SHUT_RDWR) {
+    return shutdown(fd, 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
@@ -411,97 +411,81 @@
 __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(borrowed_fd fd, void* buf, size_t len) {
-    return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
-}
-
-static __inline__ int adb_pread(int fd, void* buf, size_t len, off64_t offset) {
-#if defined(__APPLE__)
-    return TEMP_FAILURE_RETRY(pread(fd, buf, len, offset));
-#else
-    return TEMP_FAILURE_RETRY(pread64(fd, buf, len, offset));
-#endif
+static __inline__  int  adb_read(int  fd, void*  buf, size_t  len)
+{
+    return TEMP_FAILURE_RETRY( read( fd, buf, len ) );
 }
 
 // Like unix_read(), but does not handle EINTR.
-static __inline__ int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
-    return read(fd.get(), buf, len);
+static __inline__ int unix_read_interruptible(int fd, void* buf, size_t len) {
+    return read(fd, buf, len);
 }
 
-#undef read
-#define read ___xxx_read
-#undef pread
-#define pread ___xxx_pread
+#undef   read
+#define  read  ___xxx_read
 
-static __inline__ int adb_write(borrowed_fd fd, const void* buf, size_t len) {
-    return TEMP_FAILURE_RETRY(write(fd.get(), buf, len));
+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_pwrite(int fd, const void* buf, size_t len, off64_t offset) {
-#if defined(__APPLE__)
-    return TEMP_FAILURE_RETRY(pwrite(fd, buf, len, offset));
-#else
-    return TEMP_FAILURE_RETRY(pwrite64(fd, buf, len, offset));
-#endif
-}
-
 #undef   write
 #define  write  ___xxx_write
-#undef pwrite
-#define pwrite ___xxx_pwrite
 
-static __inline__ int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
+static __inline__ int64_t adb_lseek(int fd, int64_t pos, int where) {
 #if defined(__APPLE__)
-    return lseek(fd.get(), pos, where);
+    return lseek(fd, pos, where);
 #else
-    return lseek64(fd.get(), pos, where);
+    return lseek64(fd, pos, where);
 #endif
 }
 #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(borrowed_fd fd) {
-    return isatty(fd.get());
+static __inline__ int unix_isatty(int fd) {
+    return isatty(fd);
 }
-#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,21 +498,22 @@
 
 int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
 
-static __inline__ int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr,
-                                        socklen_t* addrlen) {
+static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
+{
     int fd;
 
-    fd = TEMP_FAILURE_RETRY(accept(serverfd.get(), addr, addrlen));
-    if (fd >= 0) close_on_exec(fd);
+    fd = TEMP_FAILURE_RETRY( accept( serverfd, 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(borrowed_fd fd) {
-    return socket_get_local_port(fd.get());
+inline int adb_socket_get_local_port(int fd) {
+    return socket_get_local_port(fd);
 }
 
 // Operate on a file descriptor returned from unix_open() or a well-known file
@@ -539,10 +524,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_read   adb_read
+#define  unix_write  adb_write
 #define unix_lseek adb_lseek
-#define unix_close adb_close
+#define  unix_close  adb_close
 
 static __inline__ int adb_thread_setname(const std::string& name) {
 #ifdef __APPLE__
@@ -557,31 +542,34 @@
 #endif
 }
 
-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);
+static __inline__ int  adb_setsockopt( int  fd, int  level, int  optname, const void*  optval, socklen_t  optlen )
+{
+    return setsockopt( fd, 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) {
@@ -590,32 +578,29 @@
 
 #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__ int adb_get_os_handle(borrowed_fd fd) {
-    return fd.get();
-}
-
 #endif /* !_WIN32 */
 
-static inline void disable_tcp_nagle(borrowed_fd fd) {
+static inline void disable_tcp_nagle(int fd) {
     int off = 1;
-    adb_setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
+    adb_setsockopt(fd, 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(borrowed_fd fd, int interval_sec);
+bool set_tcp_keepalive(int 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/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index c5c2275..4de240e 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -104,13 +104,13 @@
     socklen_t addrlen = sizeof(addr_storage);
     sockaddr* addr = (ipv6 ? loopback_addr6 : loopback_addr4)(&addr_storage, &addrlen, port);
 
-    if (bind(s.get(), addr, addrlen) != 0) {
+    if (bind(s, addr, addrlen) != 0) {
         set_error(error);
         return -1;
     }
 
     if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
-        if (listen(s.get(), SOMAXCONN) != 0) {
+        if (listen(s, SOMAXCONN) != 0) {
             set_error(error);
             return -1;
         }
diff --git a/adb/sysdeps/uio.h b/adb/sysdeps/uio.h
index ced884b..d06ef89 100644
--- a/adb/sysdeps/uio.h
+++ b/adb/sysdeps/uio.h
@@ -18,8 +18,6 @@
 
 #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)
@@ -28,15 +26,13 @@
     void* iov_base;
 };
 
-ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt);
+ssize_t adb_writev(int 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);
-}
+#define adb_writev writev
 
 #endif
 
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index 0f4b39c..79cebe6 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -25,21 +25,6 @@
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
 
-#if defined(_WIN32)
-#include <windows.h>
-static bool IsWine() {
-    HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
-    if (!ntdll) {
-        return false;
-    }
-    return GetProcAddress(ntdll, "wine_get_version") != nullptr;
-}
-#else
-static bool IsWine() {
-    return false;
-}
-#endif
-
 TEST(sysdeps_socketpair, smoke) {
     int fds[2];
     ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
@@ -197,10 +182,8 @@
 
     EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
 
-    if (!IsWine()) {
-        // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
-        EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
-    }
+    // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
+    EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
 }
 
 TEST_F(sysdeps_poll, fd_count) {
diff --git a/adb/sysdeps_unix.cpp b/adb/sysdeps_unix.cpp
index 3fdc917..4445a44 100644
--- a/adb/sysdeps_unix.cpp
+++ b/adb/sysdeps_unix.cpp
@@ -16,7 +16,7 @@
 
 #include "sysdeps.h"
 
-bool set_tcp_keepalive(borrowed_fd fd, int interval_sec) {
+bool set_tcp_keepalive(int 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 4d6cf3d..4c5d8cb 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -60,7 +60,6 @@
     int (*_fh_read)(FH, void*, int);
     int (*_fh_write)(FH, const void*, int);
     int (*_fh_writev)(FH, const adb_iovec*, int);
-    intptr_t (*_fh_get_os_handle)(FH);
 } FHClassRec;
 
 static void _fh_file_init(FH);
@@ -69,11 +68,14 @@
 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 intptr_t _fh_file_get_os_handle(FH f);
 
 static const FHClassRec _fh_file_class = {
-        _fh_file_init,  _fh_file_close,  _fh_file_lseek,         _fh_file_read,
-        _fh_file_write, _fh_file_writev, _fh_file_get_os_handle,
+    _fh_file_init,
+    _fh_file_close,
+    _fh_file_lseek,
+    _fh_file_read,
+    _fh_file_write,
+    _fh_file_writev,
 };
 
 static void _fh_socket_init(FH);
@@ -82,11 +84,14 @@
 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 intptr_t _fh_socket_get_os_handle(FH f);
 
 static const FHClassRec _fh_socket_class = {
-        _fh_socket_init,  _fh_socket_close,  _fh_socket_lseek,         _fh_socket_read,
-        _fh_socket_write, _fh_socket_writev, _fh_socket_get_os_handle,
+    _fh_socket_init,
+    _fh_socket_close,
+    _fh_socket_lseek,
+    _fh_socket_read,
+    _fh_socket_write,
+    _fh_socket_writev,
 };
 
 #if defined(assert)
@@ -140,14 +145,16 @@
 static  FHRec        _win32_fhs[ WIN32_MAX_FHS ];
 static  int          _win32_fh_next;  // where to start search for free FHRec
 
-static FH _fh_from_int(borrowed_fd bfd, const char* func) {
-    FH f;
+static FH
+_fh_from_int( int   fd, 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 nullptr;
     }
@@ -155,7 +162,8 @@
     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 nullptr;
     }
@@ -163,15 +171,20 @@
     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 = nullptr;
+static FH
+_fh_alloc( FHClass  clazz )
+{
+    FH   f = nullptr;
 
     std::lock_guard<std::mutex> lock(_win32_lock);
 
@@ -193,7 +206,10 @@
     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);
@@ -326,10 +342,6 @@
     return li.QuadPart;
 }
 
-static intptr_t _fh_file_get_os_handle(FH f) {
-    return reinterpret_cast<intptr_t>(f->u.handle);
-}
-
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -444,7 +456,7 @@
     return _fh_to_int(f);
 }
 
-int adb_read(borrowed_fd fd, void* buf, int len) {
+int adb_read(int fd, void* buf, int len) {
     FH f = _fh_from_int(fd, __func__);
 
     if (f == nullptr) {
@@ -455,27 +467,7 @@
     return f->clazz->_fh_read(f, buf, len);
 }
 
-int adb_pread(borrowed_fd fd, void* buf, int len, off64_t offset) {
-    OVERLAPPED overlapped = {};
-    overlapped.Offset = static_cast<DWORD>(offset);
-    overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
-    DWORD bytes_read;
-    if (!::ReadFile(adb_get_os_handle(fd), buf, static_cast<DWORD>(len), &bytes_read,
-                    &overlapped)) {
-        D("adb_pread: could not read %d bytes from FD %d", len, fd.get());
-        switch (::GetLastError()) {
-            case ERROR_IO_PENDING:
-                errno = EAGAIN;
-                return -1;
-            default:
-                errno = EINVAL;
-                return -1;
-        }
-    }
-    return static_cast<int>(bytes_read);
-}
-
-int adb_write(borrowed_fd fd, const void* buf, int len) {
+int adb_write(int fd, const void* buf, int len) {
     FH f = _fh_from_int(fd, __func__);
 
     if (f == nullptr) {
@@ -486,7 +478,7 @@
     return f->clazz->_fh_write(f, buf, len);
 }
 
-ssize_t adb_writev(borrowed_fd fd, const adb_iovec* iov, int iovcnt) {
+ssize_t adb_writev(int fd, const adb_iovec* iov, int iovcnt) {
     FH f = _fh_from_int(fd, __func__);
 
     if (f == nullptr) {
@@ -497,26 +489,7 @@
     return f->clazz->_fh_writev(f, iov, iovcnt);
 }
 
-int adb_pwrite(borrowed_fd fd, const void* buf, int len, off64_t offset) {
-    OVERLAPPED params = {};
-    params.Offset = static_cast<DWORD>(offset);
-    params.OffsetHigh = static_cast<DWORD>(offset >> 32);
-    DWORD bytes_written = 0;
-    if (!::WriteFile(adb_get_os_handle(fd), buf, len, &bytes_written, &params)) {
-        D("adb_pwrite: could not write %d bytes to FD %d", len, fd.get());
-        switch (::GetLastError()) {
-            case ERROR_IO_PENDING:
-                errno = EAGAIN;
-                return -1;
-            default:
-                errno = EINVAL;
-                return -1;
-        }
-    }
-    return static_cast<int>(bytes_written);
-}
-
-int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
+int64_t adb_lseek(int fd, int64_t pos, int where) {
     FH f = _fh_from_int(fd, __func__);
     if (!f) {
         errno = EBADF;
@@ -538,20 +511,6 @@
     return 0;
 }
 
-HANDLE adb_get_os_handle(borrowed_fd fd) {
-    FH f = _fh_from_int(fd, __func__);
-
-    if (!f) {
-        errno = EBADF;
-        return nullptr;
-    }
-
-    D("adb_get_os_handle: %s", f->name);
-    const intptr_t intptr_handle = f->clazz->_fh_get_os_handle(f);
-    const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
-    return handle;
-}
-
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -662,6 +621,15 @@
 
 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) {
+            // If the socket is not connected, this returns an error. We want to
+            // minimize logging spam, so don't log these errors for now.
+#if 0
+            D("socket shutdown failed: %s",
+              android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+#endif
+        }
         if (closesocket(f->fh_socket) == SOCKET_ERROR) {
             // Don't set errno here, since adb_close will ignore it.
             const DWORD err = WSAGetLastError();
@@ -740,16 +708,12 @@
               android::base::SystemErrorCodeToString(err).c_str());
         }
         _socket_set_errno(err);
-        return -1;
+        result = -1;
     }
     CHECK_GE(static_cast<DWORD>(std::numeric_limits<int>::max()), bytes_written);
     return static_cast<int>(bytes_written);
 }
 
-static intptr_t _fh_socket_get_os_handle(FH f) {
-    return f->u.socket;
-}
-
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -1009,11 +973,11 @@
 }
 
 #undef accept
-int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen) {
+int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t* addrlen) {
     FH serverfh = _fh_from_int(serverfd, __func__);
 
     if (!serverfh || serverfh->clazz != &_fh_socket_class) {
-        D("adb_socket_accept: invalid fd %d", serverfd.get());
+        D("adb_socket_accept: invalid fd %d", serverfd);
         errno = EBADF;
         return -1;
     }
@@ -1028,7 +992,7 @@
     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.get()
+        LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd
                    << " failed: " + android::base::SystemErrorCodeToString(err);
         _socket_set_errno(err);
         return -1;
@@ -1036,16 +1000,16 @@
 
     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.get(), fd);
+    D("adb_socket_accept on fd %d returns fd %d", serverfd, fd);
     fh.release();
     return fd;
 }
 
-int adb_setsockopt(borrowed_fd fd, int level, int optname, const void* optval, socklen_t optlen) {
+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.get());
+        D("adb_setsockopt: invalid fd %d", fd);
         errno = EBADF;
         return -1;
     }
@@ -1058,7 +1022,7 @@
         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.get(), level,
+        D("adb_setsockopt: setsockopt on fd %d level %d optname %d failed: %s\n", fd, level,
           optname, android::base::SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         result = -1;
@@ -1066,11 +1030,11 @@
     return result;
 }
 
-static int adb_getsockname(borrowed_fd fd, struct sockaddr* sockaddr, socklen_t* optlen) {
+int adb_getsockname(int 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.get());
+        D("adb_getsockname: invalid fd %d", fd);
         errno = EBADF;
         return -1;
     }
@@ -1078,7 +1042,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.get(),
+        D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
           android::base::SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         result = -1;
@@ -1086,7 +1050,7 @@
     return result;
 }
 
-int adb_socket_get_local_port(borrowed_fd fd) {
+int adb_socket_get_local_port(int fd) {
     sockaddr_storage addr_storage;
     socklen_t addr_len = sizeof(addr_storage);
 
@@ -1104,11 +1068,11 @@
     return ntohs(reinterpret_cast<sockaddr_in*>(&addr_storage)->sin_port);
 }
 
-int adb_shutdown(borrowed_fd fd, int direction) {
+int adb_shutdown(int fd, int direction) {
     FH f = _fh_from_int(fd, __func__);
 
     if (!f || f->clazz != &_fh_socket_class) {
-        D("adb_shutdown: invalid fd %d", fd.get());
+        D("adb_shutdown: invalid fd %d", fd);
         errno = EBADF;
         return -1;
     }
@@ -1116,7 +1080,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.get(),
+        D("socket shutdown fd %d failed: %s", fd,
           android::base::SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         return -1;
@@ -1174,12 +1138,12 @@
     return -1;
 }
 
-bool set_file_block_mode(borrowed_fd fd, bool block) {
+bool set_file_block_mode(int 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.get());
+        D("Setting nonblocking on bad file descriptor %d", fd);
         return false;
     }
 
@@ -1188,22 +1152,22 @@
         if (ioctlsocket(fh->u.socket, FIONBIO, &x) != 0) {
             int error = WSAGetLastError();
             _socket_set_errno(error);
-            D("Setting %d nonblocking failed (%d)", fd.get(), error);
+            D("Setting %d nonblocking failed (%d)", fd, error);
             return false;
         }
         return true;
     } else {
         errno = ENOTSOCK;
-        D("Setting nonblocking on non-socket %d", fd.get());
+        D("Setting nonblocking on non-socket %d", fd);
         return false;
     }
 }
 
-bool set_tcp_keepalive(borrowed_fd fd, int interval_sec) {
+bool set_tcp_keepalive(int 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.get());
+        D("set_tcp_keepalive(%d) failed: invalid fd", fd);
         errno = EBADF;
         return false;
     }
@@ -1217,7 +1181,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.get(),
+        D("set_tcp_keepalive(%d) failed: %s", fd,
           android::base::SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         return false;
@@ -1264,12 +1228,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(borrowed_fd fd, DWORD* mode = nullptr) {
+static HANDLE _get_console_handle(int 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.get())) {
+    if (!isatty(fd)) {
         return nullptr;
     }
 #pragma pop_macro("isatty")
@@ -1277,7 +1241,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.get());
+    const intptr_t intptr_handle = _get_osfhandle(fd);
     if (intptr_handle == -1) {
         return nullptr;
     }
@@ -1301,7 +1265,7 @@
     return _get_console_handle(fd);
 }
 
-int unix_isatty(borrowed_fd fd) {
+int unix_isatty(int fd) {
     return _get_console_handle(fd) ? 1 : 0;
 }
 
@@ -1681,7 +1645,7 @@
 
 // Prefix the len bytes in buf with the escape character, and then return the
 // new buffer length.
-static size_t _escape_prefix(char* const buf, const size_t len) {
+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) {
@@ -2109,7 +2073,7 @@
 }
 
 // Called by 'adb shell' and 'adb exec-in' (via unix_read()) to read from stdin.
-int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
+int unix_read_interruptible(int 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
@@ -2129,7 +2093,7 @@
         // plain read() in favor of unix_read() or adb_read().
 #pragma push_macro("read")
 #undef read
-        return read(fd.get(), buf, len);
+        return read(fd, buf, len);
 #pragma pop_macro("read")
     }
 }
@@ -2653,9 +2617,7 @@
 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);
-
-    // Avoid destructing NarrowArgs: argv might have been mutated to point to string literals.
-    _exit(main(argc, narrow_args.data()));
+    return main(argc, narrow_args.data());
 }
 
 // Shadow UTF-8 environment variable name/value pairs that are created from
diff --git a/adb/test_device.py b/adb/test_device.py
index 57925e8..f95a5b3 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -139,25 +139,6 @@
         msg = self.device.forward_list()
         self.assertEqual('', msg.strip())
 
-    def test_forward_old_protocol(self):
-        serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
-
-        msg = self.device.forward_list()
-        self.assertEqual('', msg.strip(),
-                         'Forwarding list must be empty to run this test.')
-
-        s = socket.create_connection(("localhost", 5037))
-        service = b"host-serial:%s:forward:tcp:5566;tcp:6655" % serialno
-        cmd = b"%04x%s" % (len(service), service)
-        s.sendall(cmd)
-
-        msg = self.device.forward_list()
-        self.assertTrue(re.search(r'tcp:5566.+tcp:6655', msg))
-
-        self.device.forward_remove_all()
-        msg = self.device.forward_list()
-        self.assertEqual('', msg.strip())
-
     def test_forward_tcp_port_0(self):
         self.assertEqual('', self.device.forward_list().strip(),
                          'Forwarding list must be empty to run this test.')
@@ -903,20 +884,6 @@
             remote_path += '/filename'
             self.device.push(local=tmp_file.name, remote=remote_path)
 
-    def disabled_test_push_multiple_slash_root(self):
-        """Regression test for pushing to //data/local/tmp.
-
-        Bug: http://b/141311284
-
-        Disabled because this broken on the adbd side as well: b/141943968
-        """
-        with tempfile.NamedTemporaryFile() as tmp_file:
-            tmp_file.write('\0' * 1024 * 1024)
-            tmp_file.flush()
-            remote_path = '/' + self.DEVICE_TEMP_DIR + '/test_push_multiple_slash_root'
-            self.device.shell(['rm', '-rf', remote_path])
-            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()
diff --git a/adb/tools/Android.bp b/adb/tools/Android.bp
deleted file mode 100644
index 71e32b7..0000000
--- a/adb/tools/Android.bp
+++ /dev/null
@@ -1,36 +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.
-
-cc_binary_host {
-    name: "check_ms_os_desc",
-
-    defaults: ["adb_defaults"],
-
-    srcs: [
-        "check_ms_os_desc.cpp",
-    ],
-
-    static_libs: [
-        "libbase",
-        "libusb",
-    ],
-
-    stl: "libc++_static",
-
-    dist: {
-        targets: [
-            "sdk",
-        ],
-    },
-}
diff --git a/adb/tools/check_ms_os_desc.cpp b/adb/tools/check_ms_os_desc.cpp
deleted file mode 100644
index 8743ff7..0000000
--- a/adb/tools/check_ms_os_desc.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * 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 <err.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <libusb/libusb.h>
-
-static bool is_adb_device(libusb_device* device) {
-    libusb_device_descriptor device_desc;
-    libusb_get_device_descriptor(device, &device_desc);
-    if (device_desc.bDeviceClass != 0) {
-        return false;
-    }
-
-    libusb_config_descriptor* config_desc;
-    int rc = libusb_get_active_config_descriptor(device, &config_desc);
-    if (rc != 0) {
-        fprintf(stderr, "failed to get config descriptor for device %u:%u: %s\n",
-                libusb_get_bus_number(device), libusb_get_port_number(device),
-                libusb_error_name(rc));
-        return false;
-    }
-
-    for (size_t i = 0; i < config_desc->bNumInterfaces; ++i) {
-        const libusb_interface* interface = &config_desc->interface[i];
-        for (int j = 0; j < interface->num_altsetting; ++j) {
-            const libusb_interface_descriptor* interface_descriptor = &interface->altsetting[j];
-            if (interface_descriptor->bInterfaceClass == 0xff &&
-                interface_descriptor->bInterfaceSubClass == 0x42 &&
-                interface_descriptor->bInterfaceProtocol == 1) {
-                return true;
-            }
-        }
-    }
-
-    return false;
-}
-
-static std::optional<std::vector<uint8_t>> get_descriptor(libusb_device_handle* handle,
-                                                          uint8_t type, uint8_t index,
-                                                          uint16_t length) {
-    std::vector<uint8_t> result;
-    result.resize(length);
-    int rc = libusb_get_descriptor(handle, type, index, result.data(), result.size());
-    if (rc < 0) {
-        fprintf(stderr, "libusb_get_descriptor failed: %s\n", libusb_error_name(rc));
-        return std::nullopt;
-    }
-    result.resize(rc);
-    return result;
-}
-
-static std::optional<std::string> get_string_descriptor(libusb_device_handle* handle,
-                                                        uint8_t index) {
-    std::string result;
-    result.resize(4096);
-    int rc = libusb_get_string_descriptor_ascii(
-            handle, index, reinterpret_cast<uint8_t*>(result.data()), result.size());
-    if (rc < 0) {
-        fprintf(stderr, "libusb_get_string_descriptor_ascii failed: %s\n", libusb_error_name(rc));
-        return std::nullopt;
-    }
-    result.resize(rc);
-    return result;
-}
-
-static void check_ms_os_desc_v1(libusb_device_handle* device_handle, const std::string& serial) {
-    auto os_desc = get_descriptor(device_handle, 0x03, 0xEE, 0x12);
-    if (!os_desc) {
-        errx(1, "failed to retrieve MS OS descriptor");
-    }
-
-    if (os_desc->size() != 0x12) {
-        errx(1, "os descriptor size mismatch");
-    }
-
-    if (memcmp(os_desc->data() + 2, u"MSFT100\0", 14) != 0) {
-        errx(1, "os descriptor signature mismatch");
-    }
-
-    uint8_t vendor_code = (*os_desc)[16];
-    uint8_t pad = (*os_desc)[17];
-
-    if (pad != 0) {
-        errx(1, "os descriptor padding non-zero");
-    }
-
-    std::vector<uint8_t> data;
-    data.resize(0x10);
-    int rc = libusb_control_transfer(device_handle, 0xC0, vendor_code, 0x00, 0x04, data.data(),
-                                     data.size(), 0);
-    if (rc != 0x10) {
-        errx(1, "failed to retrieve MS OS v1 compat descriptor header: %s", libusb_error_name(rc));
-    }
-
-    struct __attribute__((packed)) ms_os_desc_v1_header {
-        uint32_t dwLength;
-        uint16_t bcdVersion;
-        uint16_t wIndex;
-        uint8_t bCount;
-        uint8_t reserved[7];
-    };
-    static_assert(sizeof(ms_os_desc_v1_header) == 0x10);
-
-    ms_os_desc_v1_header hdr;
-    memcpy(&hdr, data.data(), data.size());
-
-    data.resize(hdr.dwLength);
-    rc = libusb_control_transfer(device_handle, 0xC0, vendor_code, 0x00, 0x04, data.data(),
-                                 data.size(), 0);
-    if (static_cast<size_t>(rc) != data.size()) {
-        errx(1, "failed to retrieve MS OS v1 compat descriptor: %s", libusb_error_name(rc));
-    }
-
-    memcpy(&hdr, data.data(), data.size());
-
-    struct __attribute__((packed)) ms_os_desc_v1_function {
-        uint8_t bFirstInterfaceNumber;
-        uint8_t reserved1;
-        uint8_t compatibleID[8];
-        uint8_t subCompatibleID[8];
-        uint8_t reserved2[6];
-    };
-
-    if (sizeof(ms_os_desc_v1_header) + hdr.bCount * sizeof(ms_os_desc_v1_function) != data.size()) {
-        errx(1, "MS OS v1 compat descriptor size mismatch");
-    }
-
-    for (int i = 0; i < hdr.bCount; ++i) {
-        ms_os_desc_v1_function function;
-        memcpy(&function,
-               data.data() + sizeof(ms_os_desc_v1_header) + i * sizeof(ms_os_desc_v1_function),
-               sizeof(function));
-        if (memcmp("WINUSB\0\0", function.compatibleID, 8) == 0) {
-            return;
-        }
-    }
-
-    errx(1, "failed to find v1 MS OS descriptor specifying WinUSB for device %s", serial.c_str());
-}
-
-static void check_ms_os_desc_v2(libusb_device_handle* device_handle, const std::string& serial) {
-    libusb_bos_descriptor* bos;
-    int rc = libusb_get_bos_descriptor(device_handle, &bos);
-
-    if (rc != 0) {
-        fprintf(stderr, "failed to get bos descriptor for device %s\n", serial.c_str());
-        return;
-    }
-
-    for (size_t i = 0; i < bos->bNumDeviceCaps; ++i) {
-        libusb_bos_dev_capability_descriptor* desc = bos->dev_capability[i];
-        if (desc->bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) {
-            errx(1, "invalid BOS descriptor type: %d", desc->bDescriptorType);
-        }
-
-        if (desc->bDevCapabilityType != 0x05 /* PLATFORM */) {
-            fprintf(stderr, "skipping non-platform dev capability: %#02x\n",
-                    desc->bDevCapabilityType);
-            continue;
-        }
-
-        if (desc->bLength < sizeof(*desc) + 16) {
-            errx(1, "received device capability descriptor not long enough to contain a UUID?");
-        }
-
-        char uuid[16];
-        memcpy(uuid, desc->dev_capability_data, 16);
-
-        constexpr uint8_t ms_os_uuid[16] = {0xD8, 0xDD, 0x60, 0xDF, 0x45, 0x89, 0x4C, 0xC7,
-                                            0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F};
-        if (memcmp(uuid, ms_os_uuid, 16) != 0) {
-            fprintf(stderr, "skipping unknown UUID\n");
-            continue;
-        }
-
-        size_t data_length = desc->bLength - sizeof(*desc) - 16;
-        fprintf(stderr, "found MS OS 2.0 descriptor, length = %zu\n", data_length);
-
-        // Linux does not appear to support MS OS 2.0 Descriptors.
-        // TODO: If and when it does, verify that we're emitting them properly.
-    }
-}
-
-int main(int argc, char** argv) {
-    libusb_context* ctx;
-    if (libusb_init(&ctx) != 0) {
-        errx(1, "failed to initialize libusb context");
-    }
-
-    libusb_device** device_list = nullptr;
-    ssize_t device_count = libusb_get_device_list(ctx, &device_list);
-    if (device_count < 0) {
-        errx(1, "libusb_get_device_list failed");
-    }
-
-    const char* expected_serial = getenv("ANDROID_SERIAL");
-    bool found = false;
-
-    for (ssize_t i = 0; i < device_count; ++i) {
-        libusb_device* device = device_list[i];
-        if (!is_adb_device(device)) {
-            continue;
-        }
-
-        libusb_device_handle* device_handle = nullptr;
-        int rc = libusb_open(device, &device_handle);
-        if (rc != 0) {
-            fprintf(stderr, "failed to open device %u:%u: %s\n", libusb_get_bus_number(device),
-                    libusb_get_port_number(device), libusb_error_name(rc));
-            continue;
-        }
-
-        libusb_device_descriptor device_desc;
-        libusb_get_device_descriptor(device, &device_desc);
-
-        std::optional<std::string> serial =
-                get_string_descriptor(device_handle, device_desc.iSerialNumber);
-        if (!serial) {
-            errx(1, "failed to get serial for device %u:%u", libusb_get_bus_number(device),
-                 libusb_get_port_number(device));
-        }
-
-        if (expected_serial && *serial != expected_serial) {
-            fprintf(stderr, "skipping %s (wanted %s)\n", serial->c_str(), expected_serial);
-            continue;
-        }
-
-        // Check for MS OS Descriptor v1.
-        // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeusb/c2f351f9-84d2-4a1b-9fe3-a6ca195f84d0
-        fprintf(stderr, "fetching v1 OS descriptor from device %s\n", serial->c_str());
-        check_ms_os_desc_v1(device_handle, *serial);
-        fprintf(stderr, "found v1 OS descriptor for device %s\n", serial->c_str());
-
-        // Read BOS for MS OS Descriptor 2.0 descriptors:
-        // http://download.microsoft.com/download/3/5/6/3563ED4A-F318-4B66-A181-AB1D8F6FD42D/MS_OS_2_0_desc.docx
-        fprintf(stderr, "fetching v2 OS descriptor from device %s\n", serial->c_str());
-        check_ms_os_desc_v2(device_handle, *serial);
-
-        found = true;
-    }
-
-    if (expected_serial && !found) {
-        errx(1, "failed to find device with serial %s", expected_serial);
-    }
-    return 0;
-}
diff --git a/adb/transport.cpp b/adb/transport.cpp
index d9749ac..841865a 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -49,7 +49,7 @@
 #include "adb_io.h"
 #include "adb_trace.h"
 #include "adb_utils.h"
-#include "fdevent/fdevent.h"
+#include "fdevent.h"
 #include "sysdeps/chrono.h"
 
 using android::base::ScopedLockAssertion;
@@ -73,7 +73,6 @@
 const char* const kFeatureAbb = "abb";
 const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
 const char* const kFeatureAbbExec = "abb_exec";
-const char* const kFeatureRemountShell = "remount_shell";
 
 namespace {
 
@@ -543,7 +542,9 @@
     // for the first time, even if no update occurred.
     if (tracker->update_needed) {
         tracker->update_needed = false;
-        device_tracker_send(tracker, list_transports(tracker->long_output));
+
+        std::string transports = list_transports(tracker->long_output);
+        device_tracker_send(tracker, transports);
     }
 }
 
@@ -586,11 +587,13 @@
     update_transport_status();
 
     // Notify `adb track-devices` clients.
+    std::string transports = list_transports(false);
+
     device_tracker* tracker = device_tracker_list;
     while (tracker != nullptr) {
         device_tracker* next = tracker->next;
         // This may destroy the tracker if the connection is closed.
-        device_tracker_send(tracker, list_transports(tracker->long_output));
+        device_tracker_send(tracker, transports);
         tracker = next;
     }
 }
@@ -1050,7 +1053,6 @@
             kFeatureAbb,
             kFeatureFixedPushSymlinkTimestamp,
             kFeatureAbbExec,
-            kFeatureRemountShell,
             // 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.
diff --git a/adb/transport.h b/adb/transport.h
index 89d76b8..3473ca2 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -65,13 +65,10 @@
 extern const char* const kFeatureApex;
 // adbd has b/110953234 fixed.
 extern const char* const kFeatureFixedPushMkdir;
-// adbd supports android binder bridge (abb) in interactive mode using shell protocol.
+// adbd supports android binder bridge (abb).
 extern const char* const kFeatureAbb;
-// adbd supports abb using raw pipe.
-extern const char* const kFeatureAbbExec;
 // adbd properly updates symlink timestamps on push.
 extern const char* const kFeatureFixedPushSymlinkTimestamp;
-extern const char* const kFeatureRemountShell;
 
 TransportId NextTransportId();
 
@@ -277,11 +274,8 @@
     std::string device;
     std::string devpath;
 
-#if !ADB_HOST
     // Used to provide the key to the framework.
     std::string auth_key;
-    uint64_t auth_id;
-#endif
 
     bool IsTcpDevice() const { return type == kTransportLocal; }
 
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 00beb3a..b66f8fa 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -19,7 +19,7 @@
 #include <gtest/gtest.h>
 
 #include "adb.h"
-#include "fdevent/fdevent_test.h"
+#include "fdevent_test.h"
 
 struct TransportTest : public FdeventTest {};
 
diff --git a/adb/types.h b/adb/types.h
index 8bd66be..cd1366d 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <string.h>
-
 #include <algorithm>
 #include <deque>
 #include <memory>
diff --git a/base/Android.bp b/base/Android.bp
index 25ae535..38f301a 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,14 +20,8 @@
         "-Wall",
         "-Werror",
         "-Wextra",
+        "-D_FILE_OFFSET_BITS=64",
     ],
-    target: {
-        android: {
-            cflags: [
-                "-D_FILE_OFFSET_BITS=64",
-            ],
-        },
-    },
 }
 
 cc_library_headers {
@@ -35,7 +29,6 @@
     vendor_available: true,
     recovery_available: true,
     host_supported: true,
-    native_bridge_supported: true,
     export_include_dirs: ["include"],
 
     target: {
@@ -52,15 +45,14 @@
     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",
@@ -108,7 +100,6 @@
     vendor_available: true,
     recovery_available: true,
     host_supported: true,
-    native_bridge_supported: true,
     vndk: {
         enabled: true,
         support_system_process: true,
@@ -117,9 +108,6 @@
         "libbase_headers",
     ],
     export_header_lib_headers: ["libbase_headers"],
-    static_libs: ["fmtlib"],
-    whole_static_libs: ["fmtlib"],
-    export_static_lib_headers: ["fmtlib"],
 }
 
 cc_library_static {
@@ -128,9 +116,6 @@
     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
@@ -143,7 +128,6 @@
         "cmsg_test.cpp",
         "endian_test.cpp",
         "errors_test.cpp",
-        "expected_test.cpp",
         "file_test.cpp",
         "logging_test.cpp",
         "macros_test.cpp",
@@ -151,9 +135,8 @@
         "parsedouble_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
-        "process_test.cpp",
         "properties_test.cpp",
-        "result_test.cpp",
+        "quick_exit_test.cpp",
         "scopeguard_test.cpp",
         "stringprintf_test.cpp",
         "strings_test.cpp",
@@ -188,21 +171,3 @@
     },
     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/README.md b/base/README.md
deleted file mode 100644
index 2ef5c10..0000000
--- a/base/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# libbase
-
-## Who is this library for?
-
-This library is a collection of convenience functions to make common tasks
-easier and less error-prone.
-
-In this context, "error-prone" covers both "hard to do correctly" and
-"hard to do with good performance", but as a general purpose library,
-libbase's primary focus is on making it easier to do things easily and
-correctly when a compromise has to be made between "simplest API" on the
-one hand and "fastest implementation" on the other. Though obviously
-the ideal is to have both.
-
-## Should my routine be added?
-
-The intention is to cover the 80% use cases, not be all things to all users.
-
-If you have a routine that's really useful in your project,
-congratulations. But that doesn't mean it should be here rather than
-just in your project.
-
-The question for libbase is "should everyone be doing this?"/"does this
-make everyone's code cleaner/safer?". Historically we've considered the
-bar for inclusion to be "are there at least three *unrelated* projects
-that would be cleaned up by doing so".
-
-If your routine is actually something from a future C++ standard (that
-isn't yet in libc++), or it's widely used in another library, that helps
-show that there's precedent. Being able to say "so-and-so has used this
-API for n years" is a good way to reduce concerns about API choices.
-
-## Any other restrictions?
-
-Unlike most Android code, code in libbase has to build for Mac and
-Windows too.
-
-Code here is also expected to have good test coverage.
-
-By its nature, it's difficult to change libbase API. It's often best
-to start using your routine just in your project, and let it "graduate"
-after you're certain that the API is solid.
diff --git a/base/abi_compatibility.cpp b/base/abi_compatibility.cpp
deleted file mode 100644
index 06a7801..0000000
--- a/base/abi_compatibility.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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
index 1fa873c..42866f8 100644
--- a/base/cmsg.cpp
+++ b/base/cmsg.cpp
@@ -29,7 +29,7 @@
 namespace android {
 namespace base {
 
-ssize_t SendFileDescriptorVector(borrowed_fd sockfd, const void* data, size_t len,
+ssize_t SendFileDescriptorVector(int 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());
@@ -67,10 +67,10 @@
   int flags = 0;
 #endif
 
-  return TEMP_FAILURE_RETRY(sendmsg(sockfd.get(), &msg, flags));
+  return TEMP_FAILURE_RETRY(sendmsg(sockfd, &msg, flags));
 }
 
-ssize_t ReceiveFileDescriptorVector(borrowed_fd sockfd, void* data, size_t len, size_t max_fds,
+ssize_t ReceiveFileDescriptorVector(int sockfd, void* data, size_t len, size_t max_fds,
                                     std::vector<unique_fd>* fds) {
   fds->clear();
 
@@ -98,7 +98,7 @@
   flags |= MSG_CMSG_CLOEXEC | MSG_NOSIGNAL;
 #endif
 
-  ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd.get(), &msg, flags));
+  ssize_t rc = TEMP_FAILURE_RETRY(recvmsg(sockfd, &msg, flags));
 
   if (rc == -1) {
     return -1;
diff --git a/base/errors_unix.cpp b/base/errors_unix.cpp
index 48269b6..296995e 100644
--- a/base/errors_unix.cpp
+++ b/base/errors_unix.cpp
@@ -17,7 +17,6 @@
 #include "android-base/errors.h"
 
 #include <errno.h>
-#include <string.h>
 
 namespace android {
 namespace base {
diff --git a/base/expected_test.cpp b/base/expected_test.cpp
deleted file mode 100644
index a74bc1d..0000000
--- a/base/expected_test.cpp
+++ /dev/null
@@ -1,894 +0,0 @@
-/*
- * 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 6321fc6..adc8984 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -49,54 +49,29 @@
 #include "android-base/unique_fd.h"
 #include "android-base/utf8.h"
 
-namespace {
-
 #ifdef _WIN32
-static int mkstemp(char* name_template, size_t size_in_chars) {
-  std::wstring path;
-  CHECK(android::base::UTF8ToWide(name_template, &path))
-      << "path can't be converted to wchar: " << name_template;
-  if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
+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.
-  int fd = _wopen(path.c_str(), O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
-  if (fd < 0) {
-    return -1;
-  }
-
-  std::string path_utf8;
-  CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
-  CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
-      << "utf8 path can't be assigned back to name_template";
-
-  return fd;
+  return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
 }
 
-static char* mkdtemp(char* name_template, size_t size_in_chars) {
-  std::wstring path;
-  CHECK(android::base::UTF8ToWide(name_template, &path))
-      << "path can't be converted to wchar: " << name_template;
-
-  if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
+char* mkdtemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
     return nullptr;
   }
-
-  if (_wmkdir(path.c_str()) != 0) {
+  if (_mkdir(template_name) == -1) {
     return nullptr;
   }
-
-  std::string path_utf8;
-  CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
-  CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
-      << "utf8 path can't be assigned back to name_template";
-
-  return name_template;
+  return template_name;
 }
 #endif
 
+namespace {
+
 std::string GetSystemTempDir() {
 #if defined(__ANDROID__)
   const auto* tmpdir = getenv("TMPDIR");
@@ -108,20 +83,15 @@
   // so try current directory if /data/local/tmp is not accessible.
   return ".";
 #elif defined(_WIN32)
-  wchar_t tmp_dir_w[MAX_PATH];
-  DWORD result = GetTempPathW(std::size(tmp_dir_w), tmp_dir_w);  // checks TMP env
-  CHECK_NE(result, 0ul) << "GetTempPathW failed, error: " << GetLastError();
-  CHECK_LT(result, std::size(tmp_dir_w)) << "path truncated to: " << result;
+  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.
-  if (tmp_dir_w[result - 1] == L'\\') {
-    tmp_dir_w[result - 1] = L'\0';
-  }
-
-  std::string tmp_dir;
-  CHECK(android::base::WideToUTF8(tmp_dir_w, &tmp_dir)) << "path can't be converted to utf8";
-
+  CHECK_EQ(tmp_dir[result - 1], '\\');
+  tmp_dir[result - 1] = '\0';
   return tmp_dir;
 #else
   const auto* tmpdir = getenv("TMPDIR");
@@ -157,11 +127,7 @@
 
 void TemporaryFile::init(const std::string& tmp_dir) {
   snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
-#if defined(_WIN32)
-  fd = mkstemp(path, sizeof(path));
-#else
   fd = mkstemp(path);
-#endif
 }
 
 TemporaryDir::TemporaryDir() {
@@ -201,11 +167,7 @@
 
 bool TemporaryDir::init(const std::string& tmp_dir) {
   snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
-#if defined(_WIN32)
-  return (mkdtemp(path, sizeof(path)) != nullptr);
-#else
   return (mkdtemp(path) != nullptr);
-#endif
 }
 
 namespace android {
@@ -214,20 +176,20 @@
 // Versions of standard library APIs that support UTF-8 strings.
 using namespace android::base::utf8;
 
-bool ReadFdToString(borrowed_fd fd, std::string* content) {
+bool ReadFdToString(int 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.get(), &sb) != -1 && sb.st_size > 0) {
+  if (fstat(fd, &sb) != -1 && sb.st_size > 0) {
     content->reserve(sb.st_size);
   }
 
   char buf[BUFSIZ];
   ssize_t n;
-  while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
+  while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
     content->append(buf, n);
   }
   return (n == 0) ? true : false;
@@ -244,11 +206,11 @@
   return ReadFdToString(fd, content);
 }
 
-bool WriteStringToFd(const std::string& content, borrowed_fd fd) {
+bool WriteStringToFd(const std::string& content, int fd) {
   const char* p = content.data();
   size_t left = content.size();
   while (left > 0) {
-    ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, left));
+    ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, left));
     if (n == -1) {
       return false;
     }
@@ -307,11 +269,11 @@
   return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
 }
 
-bool ReadFully(borrowed_fd fd, void* data, size_t byte_count) {
+bool ReadFully(int 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.get(), p, remaining));
+    ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
     if (n <= 0) return false;
     p += n;
     remaining -= n;
@@ -322,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(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
+static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
   DWORD bytes_read;
   OVERLAPPED overlapped;
   memset(&overlapped, 0, sizeof(OVERLAPPED));
   overlapped.Offset = static_cast<DWORD>(offset);
   overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
-  if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), data,
-                static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) {
+  if (!ReadFile(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), data, static_cast<DWORD>(byte_count),
+                &bytes_read, &overlapped)) {
     // In case someone tries to read errno (since this is masquerading as a POSIX call)
     errno = EIO;
     return -1;
@@ -338,10 +300,10 @@
 }
 #endif
 
-bool ReadFullyAtOffset(borrowed_fd fd, void* data, size_t byte_count, off64_t offset) {
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset) {
   uint8_t* p = reinterpret_cast<uint8_t*>(data);
   while (byte_count > 0) {
-    ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), p, byte_count, offset));
+    ssize_t n = TEMP_FAILURE_RETRY(pread(fd, p, byte_count, offset));
     if (n <= 0) return false;
     p += n;
     byte_count -= n;
@@ -350,11 +312,11 @@
   return true;
 }
 
-bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count) {
+bool WriteFully(int fd, const void* data, size_t byte_count) {
   const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
   size_t remaining = byte_count;
   while (remaining > 0) {
-    ssize_t n = TEMP_FAILURE_RETRY(write(fd.get(), p, remaining));
+    ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
     if (n == -1) return false;
     p += n;
     remaining -= n;
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 120228d..f64e81c 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -16,25 +16,18 @@
 
 #include "android-base/file.h"
 
-#include "android-base/utf8.h"
-
 #include <gtest/gtest.h>
 
 #include <errno.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <wchar.h>
 
 #include <string>
 
 #if !defined(_WIN32)
 #include <pwd.h>
-#else
-#include <windows.h>
 #endif
 
-#include "android-base/logging.h"  // and must be after windows.h for ERROR
-
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
   errno = 0;
@@ -45,7 +38,7 @@
 
 TEST(file, ReadFileToString_WriteStringToFile) {
   TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1) << tf.path;
+  ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
     << strerror(errno);
   std::string s;
@@ -77,7 +70,7 @@
 #if !defined(_WIN32)
 TEST(file, WriteStringToFile2) {
   TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1) << tf.path;
+  ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
                                                getuid(), getgid()))
       << strerror(errno);
@@ -93,92 +86,9 @@
 }
 #endif
 
-#if defined(_WIN32)
-TEST(file, NonUnicodeCharsWindows) {
-  constexpr auto kMaxEnvVariableValueSize = 32767;
-  std::wstring old_tmp;
-  old_tmp.resize(kMaxEnvVariableValueSize);
-  old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
-  if (old_tmp.empty()) {
-    // Can't continue with empty TMP folder.
-    return;
-  }
-
-  std::wstring new_tmp = old_tmp;
-  if (new_tmp.back() != L'\\') {
-    new_tmp.push_back(L'\\');
-  }
-
-  {
-    auto path(new_tmp + L"锦绣成都\\");
-    _wmkdir(path.c_str());
-    ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
-    TemporaryFile tf;
-    ASSERT_NE(tf.fd, -1) << tf.path;
-    ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
-    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
-    std::string s;
-    ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
-    EXPECT_EQ("abc", s);
-  }
-  {
-    auto path(new_tmp + L"директория с длинным именем\\");
-    _wmkdir(path.c_str());
-    ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
-    TemporaryFile tf;
-    ASSERT_NE(tf.fd, -1) << tf.path;
-    ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
-    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
-    std::string s;
-    ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
-    EXPECT_EQ("abc", s);
-  }
-  {
-    auto path(new_tmp + L"äüöß weiß\\");
-    _wmkdir(path.c_str());
-    ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
-
-    TemporaryFile tf;
-    ASSERT_NE(tf.fd, -1) << tf.path;
-    ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
-
-    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
-
-    std::string s;
-    ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
-    EXPECT_EQ("abc", s);
-  }
-
-  SetEnvironmentVariableW(L"TMP", old_tmp.c_str());
-}
-
-TEST(file, RootDirectoryWindows) {
-  constexpr auto kMaxEnvVariableValueSize = 32767;
-  std::wstring old_tmp;
-  bool tmp_is_empty = false;
-  old_tmp.resize(kMaxEnvVariableValueSize);
-  old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
-  if (old_tmp.empty()) {
-    tmp_is_empty = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
-  }
-  SetEnvironmentVariableW(L"TMP", L"C:");
-
-  TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1) << tf.path;
-
-  SetEnvironmentVariableW(L"TMP", tmp_is_empty ? nullptr : old_tmp.c_str());
-}
-#endif
-
 TEST(file, WriteStringToFd) {
   TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1) << tf.path;
+  ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
 
   ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
@@ -190,7 +100,7 @@
 
 TEST(file, WriteFully) {
   TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1) << tf.path;
+  ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
 
   ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
@@ -209,7 +119,7 @@
 
 TEST(file, RemoveFileIfExists) {
   TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1) << tf.path;
+  ASSERT_TRUE(tf.fd != -1);
   close(tf.fd);
   tf.fd = -1;
   std::string err;
@@ -343,7 +253,7 @@
 
 TEST(file, ReadFileToString_capacity) {
   TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1) << tf.path;
+  ASSERT_TRUE(tf.fd != -1);
 
   // For a huge file, the overhead should still be small.
   std::string s;
@@ -370,7 +280,7 @@
 
 TEST(file, ReadFileToString_capacity_0) {
   TemporaryFile tf;
-  ASSERT_NE(tf.fd, -1) << tf.path;
+  ASSERT_TRUE(tf.fd != -1);
 
   // Because /proc reports its files as zero-length, we don't actually trust
   // any file that claims to be zero-length. Rather than add increasingly
diff --git a/base/format_benchmark.cpp b/base/format_benchmark.cpp
deleted file mode 100644
index 9590b23..0000000
--- a/base/format_benchmark.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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/cmsg.h b/base/include/android-base/cmsg.h
index e4197b1..7f93ddc 100644
--- a/base/include/android-base/cmsg.h
+++ b/base/include/android-base/cmsg.h
@@ -51,20 +51,20 @@
 // 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,
+ssize_t SendFileDescriptorVector(int 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,
+ssize_t ReceiveFileDescriptorVector(int 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) {
+ssize_t SendFileDescriptors(int 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.
@@ -79,7 +79,7 @@
 // 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) {
+ssize_t ReceiveFileDescriptors(int sock, void* data, size_t len, Args&&... received_fds) {
   std::vector<unique_fd*> fds;
   Append(fds, std::forward<Args>(received_fds)...);
 
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index 8fa6365..cbbd8c9 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -18,9 +18,6 @@
 
 /* A cross-platform equivalent of bionic's <sys/endian.h>. */
 
-/* For __BIONIC__ and __GLIBC__ */
-#include <sys/cdefs.h>
-
 #if defined(__BIONIC__)
 
 #include <sys/endian.h>
@@ -41,34 +38,26 @@
 #define betoh16(x) be16toh(x)
 #define betoh32(x) be32toh(x)
 #define betoh64(x) be64toh(x)
-#define letoh16(x) le16toh(x)
-#define letoh32(x) le32toh(x)
-#define letoh64(x) le64toh(x)
 
 #else
 
-#if defined(__APPLE__)
-/* macOS has some of the basics. */
-#include <sys/_endian.h>
-#else
-/* Windows has some of the basics as well. */
-#include <sys/param.h>
-#include <winsock2.h>
-/* winsock2.h *must* be included before the following four macros. */
-#define htons(x) __builtin_bswap16(x)
-#define htonl(x) __builtin_bswap32(x)
-#define ntohs(x) __builtin_bswap16(x)
-#define ntohl(x) __builtin_bswap32(x)
-#endif
-
-/* Neither macOS nor Windows have the rest. */
+/* Mac OS and Windows have nothing. */
 
 #define __LITTLE_ENDIAN 1234
-#define __BIG_ENDIAN 4321
-#define __BYTE_ORDER __LITTLE_ENDIAN
+#define LITTLE_ENDIAN __LITTLE_ENDIAN
 
+#define __BIG_ENDIAN 4321
+#define BIG_ENDIAN __BIG_ENDIAN
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#define BYTE_ORDER __BYTE_ORDER
+
+#define htons(x) __builtin_bswap16(x)
+#define htonl(x) __builtin_bswap32(x)
 #define htonq(x) __builtin_bswap64(x)
 
+#define ntohs(x) __builtin_bswap16(x)
+#define ntohl(x) __builtin_bswap32(x)
 #define ntohq(x) __builtin_bswap64(x)
 
 #define htobe16(x) __builtin_bswap16(x)
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
deleted file mode 100644
index 030ef35e..0000000
--- a/base/include/android-base/expected.h
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * 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
-
-// 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<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
-                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
-                !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_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<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
-                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
-                !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_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) noexcept(
-      std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
-
-  template <class U = T _ENABLE_IF(
-                !std::is_void_v<T> &&
-                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_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) noexcept(std::is_nothrow_move_assignable_v<E>) = 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&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
-
-  template <class Err = E _ENABLE_IF(
-                std::is_constructible_v<E, Err> &&
-                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
-                !std::is_same_v<std::remove_cv_t<std::remove_reference_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&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
-      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 c622562..f8748b5 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -21,9 +21,8 @@
 
 #include <string>
 
-#include "android-base/macros.h"
+#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. */
@@ -78,13 +77,13 @@
 namespace android {
 namespace base {
 
-bool ReadFdToString(borrowed_fd fd, std::string* content);
+bool ReadFdToString(int 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, borrowed_fd fd);
+bool WriteStringToFd(const std::string& content, int fd);
 
 #if !defined(_WIN32)
 bool WriteStringToFile(const std::string& content, const std::string& path,
@@ -92,7 +91,7 @@
                        bool follow_symlinks = false);
 #endif
 
-bool ReadFully(borrowed_fd fd, void* data, size_t byte_count);
+bool ReadFully(int fd, void* data, size_t byte_count);
 
 // Reads `byte_count` bytes from the file descriptor at the specified offset.
 // Returns false if there was an IO error or EOF was reached before reading `byte_count` bytes.
@@ -102,9 +101,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(borrowed_fd fd, void* data, size_t byte_count, off64_t offset);
+bool ReadFullyAtOffset(int fd, void* data, size_t byte_count, off64_t offset);
 
-bool WriteFully(borrowed_fd fd, const void* data, size_t byte_count);
+bool WriteFully(int fd, const void* data, size_t byte_count);
 
 bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
 
diff --git a/base/include/android-base/format.h b/base/include/android-base/format.h
deleted file mode 100644
index 330040d..0000000
--- a/base/include/android-base/format.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wshadow"
-#include <fmt/chrono.h>
-#pragma clang diagnostic pop
-#include <fmt/core.h>
-#include <fmt/format.h>
-#include <fmt/ostream.h>
-#include <fmt/printf.h>
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index ab6476c..f94cc25 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -469,7 +469,7 @@
 }  // namespace base
 }  // namespace android
 
-namespace std {  // NOLINT(cert-dcl58-cpp)
+namespace std {
 
 // Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
 //
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
index 6a19f1b..80513b1 100644
--- a/base/include/android-base/mapped_file.h
+++ b/base/include/android-base/mapped_file.h
@@ -16,29 +16,26 @@
 
 #pragma once
 
+#include "android-base/macros.h"
+#include "android-base/off64_t.h"
+
 #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
-using os_handle = HANDLE;
 #else
 #include <sys/mman.h>
-using os_handle = int;
 #endif
 
 namespace android {
 namespace base {
 
 /**
- * A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping).
+ * A region of a file mapped into memory.
  */
 class MappedFile {
  public:
@@ -47,37 +44,19 @@
    * `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);
-
-  /**
-   * Same thing, but using the raw OS file handle instead of a CRT wrapper.
-   */
-  static MappedFile FromOsHandle(os_handle h, off64_t offset, size_t length, int prot);
+  static std::unique_ptr<MappedFile> FromFd(int fd, off64_t offset, size_t length, int prot);
 
   /**
    * Removes the mapping.
    */
   ~MappedFile();
 
-  /**
-   * Not copyable but movable.
-   */
-  MappedFile(MappedFile&& other);
-  MappedFile& operator=(MappedFile&& other);
-
-  char* data() const { return base_ + offset_; }
-  size_t size() const { return size_; }
-
-  bool isValid() const { return base_ != nullptr; }
-
-  explicit operator bool() const { return isValid(); }
+  char* data() { return base_ + offset_; }
+  size_t size() { return size_; }
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
 
-  void Close();
-
   char* base_;
   size_t size_;
 
diff --git a/base/include/android-base/process.h b/base/include/android-base/process.h
deleted file mode 100644
index 69ed3fb..0000000
--- a/base/include/android-base/process.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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 31823df..31e5273 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -49,6 +49,9 @@
                                         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`.
diff --git a/init/fscrypt_init_extensions.h b/base/include/android-base/quick_exit.h
similarity index 61%
rename from init/fscrypt_init_extensions.h
rename to base/include/android-base/quick_exit.h
index d357bb2..a03b14f 100644
--- a/init/fscrypt_init_extensions.h
+++ b/base/include/android-base/quick_exit.h
@@ -16,15 +16,19 @@
 
 #pragma once
 
-#include <string>
+#include <stdlib.h>
 
-enum class FscryptAction {
-    kNone,
-    kAttempt,
-    kRequire,
-    kDeleteIfNecessary,
-};
+// Provide emulation for at_quick_exit/quick_exit on platforms that don't have it.
+namespace android {
+namespace base {
 
-bool FscryptInstallKeyring();
-bool FscryptSetDirectoryPolicy(const std::string& ref_basename, FscryptAction action,
-                               const std::string& dir);
+// Bionic and glibc have quick_exit, Darwin and Windows don't.
+#if !defined(__linux__)
+  void quick_exit(int exit_code) __attribute__((noreturn));
+  int at_quick_exit(void (*func)());
+#else
+  using ::at_quick_exit;
+  using ::quick_exit;
+#endif
+}
+}
diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h
deleted file mode 100644
index 1b763af..0000000
--- a/base/include/android-base/result.h
+++ /dev/null
@@ -1,205 +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.
- */
-
-// 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/strings.h b/base/include/android-base/strings.h
index b1c22c9..8e9716f 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -18,7 +18,6 @@
 
 #include <sstream>
 #include <string>
-#include <string_view>
 #include <vector>
 
 namespace android {
@@ -69,21 +68,5 @@
 // Tests whether 'lhs' equals 'rhs', ignoring case.
 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
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 1605daf..3fa3bea 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -92,8 +92,6 @@
   explicit unique_fd_impl(int fd) { reset(fd); }
   ~unique_fd_impl() { reset(); }
 
-  unique_fd_impl(const unique_fd_impl&) = delete;
-  void operator=(const unique_fd_impl&) = delete;
   unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
   unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
     int fd = s.fd_;
@@ -105,23 +103,11 @@
   void reset(int new_value = -1) { reset(new_value, nullptr); }
 
   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;
 
-  bool ok() const { return get() >= 0; }
-
   int release() __attribute__((warn_unused_result)) {
     tag(fd_, this, nullptr);
     int ret = fd_;
@@ -171,6 +157,9 @@
   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&);
 };
 
 using unique_fd = unique_fd_impl<DefaultCloser>;
@@ -257,22 +246,6 @@
 
 #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) {}  // NOLINT
-  template <typename T>
-  /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {}  // NOLINT
-
-  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
 
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index 862b73b..d26e8ba 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -16,15 +16,11 @@
 
 #include "android-base/mapped_file.h"
 
-#include <utility>
-
 #include <errno.h>
 
 namespace android {
 namespace base {
 
-static constexpr char kEmptyBuffer[] = {'0'};
-
 static off64_t InitPageSize() {
 #if defined(_WIN32)
   SYSTEM_INFO si;
@@ -35,88 +31,52 @@
 #endif
 }
 
-std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
-                                               int prot) {
-#if defined(_WIN32)
-  auto file =
-      FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
-#else
-  auto file = FromOsHandle(fd.get(), offset, length, prot);
-#endif
-  return file ? std::make_unique<MappedFile>(std::move(file)) : std::unique_ptr<MappedFile>{};
-}
-
-MappedFile MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length, int prot) {
-  static const off64_t page_size = InitPageSize();
+std::unique_ptr<MappedFile> MappedFile::FromFd(int 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 = CreateFileMappingW(
-      h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+  HANDLE handle =
+      CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd)), 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 && ::GetLastError() == ERROR_FILE_INVALID) {
-      return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0, nullptr};
+    if (length == 0) {
+      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
     }
-    return 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 MappedFile(nullptr, 0, 0, nullptr);
+    return nullptr;
   }
-  return MappedFile{static_cast<char*>(base), length, slop, handle};
+  return std::unique_ptr<MappedFile>(
+      new MappedFile{static_cast<char*>(base), length, slop, handle});
 #else
-  void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset);
+  void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd, 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 MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0};
+      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0});
     }
-    return MappedFile(nullptr, 0, 0);
+    return nullptr;
   }
-  return MappedFile{static_cast<char*>(base), length, slop};
+  return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
 #endif
 }
 
-MappedFile::MappedFile(MappedFile&& other)
-    : base_(std::exchange(other.base_, nullptr)),
-      size_(std::exchange(other.size_, 0)),
-      offset_(std::exchange(other.offset_, 0))
-#ifdef _WIN32
-      ,
-      handle_(std::exchange(other.handle_, nullptr))
-#endif
-{
-}
-
-MappedFile& MappedFile::operator=(MappedFile&& other) {
-  Close();
-  base_ = std::exchange(other.base_, nullptr);
-  size_ = std::exchange(other.size_, 0);
-  offset_ = std::exchange(other.offset_, 0);
-#ifdef _WIN32
-  handle_ = std::exchange(other.handle_, nullptr);
-#endif
-  return *this;
-}
-
 MappedFile::~MappedFile() {
-  Close();
-}
-
-void MappedFile::Close() {
 #if defined(_WIN32)
-  if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_);
+  if (base_ != nullptr) UnmapViewOfFile(base_);
   if (handle_ != nullptr) CloseHandle(handle_);
-  handle_ = nullptr;
 #else
-  if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_);
+  if (base_ != nullptr) munmap(base_, size_ + offset_);
 #endif
 
   base_ = nullptr;
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
index 3629108..cfde73c 100644
--- a/base/mapped_file_test.cpp
+++ b/base/mapped_file_test.cpp
@@ -44,8 +44,5 @@
   ASSERT_TRUE(tf.fd != -1);
 
   auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
-  ASSERT_NE(nullptr, m);
-  EXPECT_TRUE((bool)*m);
-  EXPECT_EQ(0u, m->size());
-  EXPECT_NE(nullptr, m->data());
+  ASSERT_EQ(0u, m->size());
 }
diff --git a/base/process.cpp b/base/process.cpp
deleted file mode 100644
index b8cabf6..0000000
--- a/base/process.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 056f667..0000000
--- a/base/process_test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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/quick_exit.cpp b/base/quick_exit.cpp
new file mode 100644
index 0000000..e4dd62b
--- /dev/null
+++ b/base/quick_exit.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/quick_exit.h"
+
+#if !defined(__linux__)
+
+#include <mutex>
+#include <vector>
+
+namespace android {
+namespace base {
+
+static auto& quick_exit_mutex = *new std::mutex();
+static auto& quick_exit_handlers = *new std::vector<void (*)()>();
+
+void quick_exit(int exit_code) {
+  std::lock_guard<std::mutex> lock(quick_exit_mutex);
+  for (auto it = quick_exit_handlers.rbegin(); it != quick_exit_handlers.rend(); ++it) {
+    (*it)();
+  }
+  _Exit(exit_code);
+}
+
+int at_quick_exit(void (*func)()) {
+  std::lock_guard<std::mutex> lock(quick_exit_mutex);
+  quick_exit_handlers.push_back(func);
+  return 0;
+}
+
+}  // namespace base
+}  // namespace android
+#endif
diff --git a/base/quick_exit_test.cpp b/base/quick_exit_test.cpp
new file mode 100644
index 0000000..7ca8156
--- /dev/null
+++ b/base/quick_exit_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/quick_exit.h"
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "android-base/test_utils.h"
+
+// These tests are a bit sketchy, since each test run adds global state that affects subsequent
+// tests (including ones not in this file!). Exit with 0 in Exiter and stick the at_quick_exit test
+// at the end to hack around this.
+struct Exiter {
+  ~Exiter() {
+    _Exit(0);
+  }
+};
+
+TEST(quick_exit, smoke) {
+  ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(123), "");
+}
+
+TEST(quick_exit, skip_static_destructors) {
+  static Exiter exiter;
+  ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(123), "");
+}
+
+TEST(quick_exit, at_quick_exit) {
+  static int counter = 4;
+  // "Functions passed to at_quick_exit are called in reverse order of their registration."
+  ASSERT_EQ(0, android::base::at_quick_exit([]() { _exit(counter); }));
+  ASSERT_EQ(0, android::base::at_quick_exit([]() { counter += 2; }));
+  ASSERT_EQ(0, android::base::at_quick_exit([]() { counter *= 10; }));
+  ASSERT_EXIT(android::base::quick_exit(123), testing::ExitedWithCode(42), "");
+}
diff --git a/base/result_test.cpp b/base/result_test.cpp
deleted file mode 100644
index 2ee4057..0000000
--- a/base/result_test.cpp
+++ /dev/null
@@ -1,422 +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 "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_test.cpp b/base/strings_test.cpp
index ca3c0b8..9d74094 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -295,19 +295,3 @@
 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/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 8979b0c..cb09433 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -1253,12 +1253,7 @@
         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
+        echo 1 reboo        # negative test (ID for unknown is 1)
         ;;
     esac
     echo ${id} "${match}"   # matches b/c of exact
@@ -1271,8 +1266,6 @@
 - (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 |
@@ -1302,8 +1295,7 @@
   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'
+    '-bootstat: Unknown boot reason: reboo'
 }
 
 [ "USAGE: ${progname} [-s SERIAL] [tests]...
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index d08a59f..7b60524 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -140,13 +140,13 @@
     {"mba_err", 13},
     {"Watchdog", 14},
     {"Panic", 15},
-    {"power_key", 16},  // aliasReasons to cold,powerkey (Mediatek)
-    {"power_on", 17},   // aliasReasons to cold,powerkey
+    {"power_key", 16},
+    {"power_on", 17},
     {"Reboot", 18},
     {"rtc", 19},
     {"edl", 20},
     {"oem_pon1", 21},
-    {"oem_powerkey", 22},  // aliasReasons to cold,powerkey
+    {"oem_powerkey", 22},
     {"oem_unknown_reset", 23},
     {"srto: HWWDT reset SC", 24},
     {"srto: HWWDT reset platform", 25},
@@ -201,15 +201,15 @@
     {"hard,hw_reset", 72},
     {"shutdown,suspend", 73},    // Suspend to RAM
     {"shutdown,hibernate", 74},  // Suspend to DISK
-    {"power_on_key", 75},        // aliasReasons to cold,powerkey
-    {"reboot_by_key", 76},       // translated to reboot,by_key
-    {"wdt_by_pass_pwk", 77},     // Mediatek
-    {"reboot_longkey", 78},      // translated to reboot,longkey
-    {"powerkey", 79},            // aliasReasons to cold,powerkey
-    {"usb", 80},                 // aliasReasons to cold,charger (Mediatek)
-    {"wdt", 81},                 // Mediatek
-    {"tool_by_pass_pwk", 82},    // aliasReasons to reboot,tool (Mediatek)
-    {"2sec_reboot", 83},         // aliasReasons to cold,rtc,2sec (Mediatek)
+    {"power_on_key", 75},
+    {"reboot_by_key", 76},
+    {"wdt_by_pass_pwk", 77},
+    {"reboot_longkey", 78},
+    {"powerkey", 79},
+    {"usb", 80},
+    {"wdt", 81},
+    {"tool_by_pass_pwk", 82},
+    {"2sec_reboot", 83},
     {"reboot,by_key", 84},
     {"reboot,longkey", 85},
     {"reboot,2sec", 86},  // Deprecate in two years, replaced with cold,rtc,2sec
@@ -219,28 +219,28 @@
     {"reboot,rescueparty", 90},
     {"charge", 91},
     {"oem_tz_crash", 92},
-    {"uvlo", 93},  // aliasReasons to reboot,undervoltage
+    {"uvlo", 93},  // aliasReasons converts to reboot,undervoltage
     {"oem_ps_hold", 94},
     {"abnormal_reset", 95},
     {"oemerr_unknown", 96},
     {"reboot_fastboot_mode", 97},
     {"watchdog_apps_bite", 98},
     {"xpu_err", 99},
-    {"power_on_usb", 100},  // aliasReasons to cold,charger
+    {"power_on_usb", 100},
     {"watchdog_rpm", 101},
     {"watchdog_nonsec", 102},
     {"watchdog_apps_bark", 103},
     {"reboot_dmverity_corrupted", 104},
-    {"reboot_smpl", 105},  // aliasReasons to reboot,powerloss
+    {"reboot_smpl", 105},  // aliasReasons converts to reboot,powerloss
     {"watchdog_sdi_apps_reset", 106},
-    {"smpl", 107},  // aliasReasons to reboot,powerloss
+    {"smpl", 107},  // aliasReasons converts to reboot,powerloss
     {"oem_modem_failed_to_powerup", 108},
     {"reboot_normal", 109},
     {"oem_lpass_cfg", 110},
     {"oem_xpu_ns_error", 111},
-    {"power_key_press", 112},  // aliasReasons to cold,powerkey
+    {"power_key_press", 112},
     {"hardware_reset", 113},
-    {"reboot_by_powerkey", 114},  // aliasReasons to cold,powerkey (is this correct?)
+    {"reboot_by_powerkey", 114},
     {"reboot_verity", 115},
     {"oem_rpm_undef_error", 116},
     {"oem_crash_on_the_lk", 117},
@@ -250,7 +250,7 @@
     {"factory_cable", 121},
     {"oem_ar6320_failed_to_powerup", 122},
     {"watchdog_rpm_bite", 123},
-    {"power_on_cable", 124},  // aliasReasons to cold,charger
+    {"power_on_cable", 124},
     {"reboot_unknown", 125},
     {"wireless_charger", 126},
     {"0x776655ff", 127},
@@ -276,10 +276,10 @@
     {"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
+    {"cold,rtc,2sec", 150},
+    {"reboot,tool", 151},
+    {"reboot,wdt", 152},
+    {"reboot,unknown", 153},
     {"kernel_panic,audit", 154},
     {"kernel_panic,atomic", 155},
     {"kernel_panic,hung", 156},
@@ -304,14 +304,6 @@
     {"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},
-    {"reboot,boringssl-self-check-failed", 185},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -840,12 +832,12 @@
     // following table smaller.
     static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
         {"watchdog", "wdog"},
+        {"cold,powerkey", "powerkey|power_key|PowerKey"},
         {"kernel_panic", "panic"},
         {"shutdown,thermal", "thermal"},
         {"warm,s3_wakeup", "s3_wakeup"},
         {"hard,hw_reset", "hw_reset"},
-        {"cold,charger", "usb|power_on_cable"},
-        {"cold,powerkey", "powerkey|power_key|PowerKey|power_on"},
+        {"cold,charger", "usb"},
         {"cold,rtc", "rtc"},
         {"cold,rtc,2sec", "2sec_reboot"},
         {"!warm", "wdt_by_pass_pwk"},  // change flavour of blunt
@@ -1182,7 +1174,6 @@
   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");
 
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 0602e0a..2e226da 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -194,7 +194,6 @@
 cc_test {
     name: "debuggerd_test",
     defaults: ["debuggerd_defaults"],
-    require_root: true,
 
     cflags: ["-Wno-missing-field-initializers"],
     srcs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 7e35a2f..60eb241 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -22,11 +22,9 @@
 #include <sys/poll.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <time.h>
 #include <unistd.h>
 
 #include <chrono>
-#include <iomanip>
 
 #include <android-base/cmsg.h>
 #include <android-base/file.h>
@@ -44,10 +42,8 @@
 
 using namespace std::chrono_literals;
 
-using android::base::ReadFileToString;
 using android::base::SendFileDescriptors;
 using android::base::unique_fd;
-using android::base::WriteStringToFd;
 
 static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
   const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
@@ -69,76 +65,6 @@
   tv->tv_usec = static_cast<long>(microseconds.count());
 }
 
-static void get_wchan_header(pid_t pid, std::stringstream& buffer) {
-  struct tm now;
-  time_t t = time(nullptr);
-  localtime_r(&t, &now);
-  char timestamp[32];
-  strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now);
-  std::string time_now(timestamp);
-
-  std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
-
-  char proc_name_buf[1024];
-  const char* proc_name = nullptr;
-  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), &fclose);
-
-  if (fp) {
-    proc_name = fgets(proc_name_buf, sizeof(proc_name_buf), fp.get());
-  }
-
-  if (!proc_name) {
-    proc_name = "<unknown>";
-  }
-
-  buffer << "\n----- Waiting Channels: pid " << pid << " at " << time_now << " -----\n"
-         << "Cmd line: " << proc_name << "\n";
-}
-
-static void get_wchan_footer(pid_t pid, std::stringstream& buffer) {
-  buffer << "----- end " << std::to_string(pid) << " -----\n";
-}
-
-/**
- * Returns the wchan data for each thread in the process,
- * or empty string if unable to obtain any data.
- */
-static std::string get_wchan_data(pid_t pid) {
-  std::stringstream buffer;
-  std::vector<pid_t> tids;
-
-  if (!android::procinfo::GetProcessTids(pid, &tids)) {
-    LOG(WARNING) << "libdebuggerd_client: Failed to get process tids";
-    return buffer.str();
-  }
-
-  std::stringstream data;
-  for (int tid : tids) {
-    std::string path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/wchan";
-    std::string wchan_str;
-    if (!ReadFileToString(path, &wchan_str, true)) {
-      PLOG(WARNING) << "libdebuggerd_client: Failed to read \"" << path << "\"";
-      continue;
-    }
-    data << "sysTid=" << std::left << std::setw(10) << tid << wchan_str << "\n";
-  }
-
-  if (std::string str = data.str(); !str.empty()) {
-    get_wchan_header(pid, buffer);
-    buffer << "\n" << str << "\n";
-    get_wchan_footer(pid, buffer);
-    buffer << "\n";
-  }
-
-  return buffer.str();
-}
-
-static void dump_wchan_data(const std::string& data, int fd, pid_t pid) {
-  if (!WriteStringToFd(data, fd)) {
-    LOG(WARNING) << "libdebuggerd_client: Failed to dump wchan data for pid: " << pid;
-  }
-}
-
 bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
                             unique_fd output_fd) {
   pid_t pid = tid;
@@ -195,10 +121,7 @@
     return false;
   }
 
-  InterceptRequest req = {
-      .dump_type = dump_type,
-      .pid = pid,
-  };
+  InterceptRequest req = {.pid = pid, .dump_type = dump_type};
   if (!set_timeout(sockfd)) {
     PLOG(ERROR) << "libdebugger_client: failed to set timeout";
     return false;
@@ -338,17 +261,6 @@
   if (copy == -1) {
     return -1;
   }
-
-  // debuggerd_trigger_dump results in every thread in the process being interrupted
-  // by a signal, so we need to fetch the wchan data before calling that.
-  std::string wchan_data = get_wchan_data(tid);
-
   int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
-  int ret = debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
-
-  // Dump wchan data, since only privileged processes (CAP_SYS_ADMIN) can read
-  // kernel stack traces (/proc/*/stack).
-  dump_wchan_data(wchan_data, fd, tid);
-
-  return ret;
+  return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
 }
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 2545cd6..9c2f0d6 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -73,15 +73,15 @@
   unique_fd pipe_read, pipe_write;
   ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
 
-  // 64 MiB should be enough for everyone.
+  // 64 kB should be enough for everyone.
   constexpr int PIPE_SIZE = 64 * 1024 * 1024;
   ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
 
   // Wait for a bit to let the child spawn all of its threads.
-  std::this_thread::sleep_for(1s);
+  std::this_thread::sleep_for(250ms);
 
   ASSERT_TRUE(
-      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 60000, std::move(pipe_write)));
+      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write)));
   // Immediately kill the forked child, to make sure that the dump didn't return early.
   ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
 
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 3041664..f0bdfbf 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -193,7 +193,6 @@
     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");
@@ -315,11 +314,6 @@
     } else if (!strcasecmp(arg, "seccomp")) {
       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_test.cpp b/debuggerd/debuggerd_test.cpp
index 99729dc..1f249c5 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -101,10 +101,7 @@
     FAIL() << "failed to contact tombstoned: " << strerror(errno);
   }
 
-  InterceptRequest req = {
-      .dump_type = intercept_type,
-      .pid = target_pid,
-  };
+  InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type};
 
   unique_fd output_pipe_write;
   if (!Pipe(output_fd, &output_pipe_write)) {
@@ -605,8 +602,6 @@
   policy += "\nclone: 1";
   policy += "\nsigaltstack: 1";
   policy += "\nnanosleep: 1";
-  policy += "\ngetrlimit: 1";
-  policy += "\nugetrlimit: 1";
 
   FILE* tmp_file = tmpfile();
   if (!tmp_file) {
@@ -1103,30 +1098,3 @@
   // This should be good enough, though...
   ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?";
 }
-
-static __attribute__((__noinline__)) void overflow_stack(void* p) {
-  void* buf[1];
-  buf[0] = p;
-  static volatile void* global = buf;
-  if (global) {
-    global = buf;
-    overflow_stack(&buf);
-  }
-}
-
-TEST_F(CrasherTest, stack_overflow) {
-  int intercept_result;
-  unique_fd output_fd;
-  StartProcess([]() { overflow_stack(nullptr); });
-
-  StartIntercept(&output_fd);
-  FinishCrasher();
-  AssertDeath(SIGSEGV);
-  FinishIntercept(&intercept_result);
-
-  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
-  std::string result;
-  ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
-}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index b90ca80..598ea85 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -525,8 +525,8 @@
   log_signal_summary(info);
 
   debugger_thread_info thread_info = {
-      .crashing_tid = __gettid(),
       .pseudothread_tid = -1,
+      .crashing_tid = __gettid(),
       .siginfo = info,
       .ucontext = context,
       .abort_msg = reinterpret_cast<uintptr_t>(abort_message),
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 9dea7ac..88c206f 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -345,9 +345,9 @@
 
 TEST_F(TombstoneTest, dump_thread_info_uid) {
   dump_thread_info(&log_, ThreadInfo{.uid = 1,
+                                     .pid = 2,
                                      .tid = 3,
                                      .thread_name = "some_thread",
-                                     .pid = 2,
                                      .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());
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 1993840..d246722 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -86,39 +86,7 @@
   _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
 }
 
-static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
-                                            unwindstack::Maps* maps) {
-  static constexpr uint64_t kMaxDifferenceBytes = 256;
-  uint64_t difference;
-  if (sp >= fault_addr) {
-    difference = sp - fault_addr;
-  } else {
-    difference = fault_addr - sp;
-  }
-  if (difference <= kMaxDifferenceBytes) {
-    // The faulting address is close to the current sp, check if the sp
-    // indicates a stack overflow.
-    // On arm, the sp does not get updated when the instruction faults.
-    // In this case, the sp will still be in a valid map, which is the
-    // last case below.
-    // On aarch64, the sp does get updated when the instruction faults.
-    // In this case, the sp will be in either an invalid map if triggered
-    // on the main thread, or in a guard map if in another thread, which
-    // will be the first case or second case from below.
-    unwindstack::MapInfo* map_info = maps->Find(sp);
-    if (map_info == nullptr) {
-      return "stack pointer is in a non-existent map; likely due to stack overflow.";
-    } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
-      return "stack pointer is not in a rw map; likely due to stack overflow.";
-    } else if ((sp - map_info->start) <= kMaxDifferenceBytes) {
-      return "stack pointer is close to top of stack; likely stack overflow.";
-    }
-  }
-  return "";
-}
-
-static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps,
-                                unwindstack::Regs* regs) {
+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)) {
@@ -133,16 +101,11 @@
       cause = "call to kuser_memory_barrier";
     } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
       cause = "call to kuser_cmpxchg64";
-    } else {
-      cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps);
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
-    uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
-    unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+    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 {
-      cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps);
     }
   } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
     cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
@@ -484,7 +447,7 @@
 
   if (thread_info.siginfo) {
     dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
-    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
+    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps());
   }
 
   if (primary_thread) {
@@ -595,7 +558,7 @@
       }
       AndroidLogEntry e;
       char buf[512];
-      if (android_log_processBinaryLogBuffer(&log_entry.entry, &e, g_eventTagMap, buf,
+      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',
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index d09b8e8..bbeb181 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -100,7 +100,7 @@
 
   static CrashQueue* for_tombstones() {
     static CrashQueue queue("/data/tombstones", "tombstone_" /* file_name_prefix */,
-                            GetIntProperty("tombstoned.max_tombstone_count", 32),
+                            GetIntProperty("tombstoned.max_tombstone_count", 10),
                             1 /* max_concurrent_dumps */);
     return &queue;
   }
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index b4a1e71..53ef01c 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -2,10 +2,10 @@
     user tombstoned
     group system
 
+    # Don't start tombstoned until after the real /data is mounted.
+    class late_start
+
     socket tombstoned_crash seqpacket 0666 system system
     socket tombstoned_intercept seqpacket 0666 system system
     socket tombstoned_java_trace seqpacket 0666 system system
     writepid /dev/cpuset/system-background/tasks
-
-on post-fs-data
-    start tombstoned
diff --git a/set-verity-state/.clang-format b/demangle/.clang-format
similarity index 100%
rename from set-verity-state/.clang-format
rename to demangle/.clang-format
diff --git a/demangle/Android.bp b/demangle/Android.bp
new file mode 100644
index 0000000..fd79cf8
--- /dev/null
+++ b/demangle/Android.bp
@@ -0,0 +1,87 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+    name: "libdemangle_defaults",
+
+    host_supported: true,
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libdemangle",
+    defaults: ["libdemangle_defaults"],
+    vendor_available: true,
+    recovery_available: true,
+
+    srcs: [
+        "Demangler.cpp",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+}
+
+cc_binary {
+    name: "demangle",
+    defaults: ["libdemangle_defaults"],
+    srcs: ["demangle.cpp"],
+    host_supported: true,
+
+    shared_libs: ["libdemangle"],
+}
+
+//-------------------------------------------------------------------------
+// Unit Tests
+//-------------------------------------------------------------------------
+cc_test {
+    name: "libdemangle_test",
+    defaults: ["libdemangle_defaults"],
+
+    srcs: [
+        "DemangleTest.cpp",
+    ],
+
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+
+    shared_libs: [
+        "libdemangle",
+    ],
+
+    test_suites: ["device-tests"],
+    required: [
+        "libdemangle",
+    ],
+}
diff --git a/demangle/Android.mk b/demangle/Android.mk
new file mode 100644
index 0000000..d8082a9
--- /dev/null
+++ b/demangle/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := demangle_fuzzer
+LOCAL_SRC_FILES := \
+    Demangler.cpp \
+    demangle_fuzzer.cpp \
+
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+    -Wextra \
+
+include $(BUILD_FUZZ_TEST)
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
new file mode 100644
index 0000000..1787031
--- /dev/null
+++ b/demangle/DemangleTest.cpp
@@ -0,0 +1,555 @@
+/*
+ * 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 <stdlib.h>
+
+#include <gtest/gtest.h>
+
+#include <demangle.h>
+
+#include "Demangler.h"
+
+TEST(DemangleTest, IllegalArgumentModifiers) {
+  Demangler demangler;
+
+  ASSERT_EQ("_Zpp4FUNKK", demangler.Parse("_Zpp4FUNKK"));
+  ASSERT_EQ("_Zpp4FUNVV", demangler.Parse("_Zpp4FUNVV"));
+}
+
+TEST(DemangleTest, VoidArgument) {
+  Demangler demangler;
+
+  ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
+  ASSERT_EQ("func(void&)", demangler.Parse("_ZN4funcERv"));
+  ASSERT_EQ("func(void, void)", demangler.Parse("_ZN4funcEvv"));
+  ASSERT_EQ("func(void*)", demangler.Parse("_ZN4funcEPv"));
+  ASSERT_EQ("func(void const)", demangler.Parse("_ZN4funcEKv"));
+  ASSERT_EQ("func(void volatile)", demangler.Parse("_ZN4funcEVv"));
+}
+
+TEST(DemangleTest, ArgumentModifiers) {
+  Demangler demangler;
+
+  ASSERT_EQ("func(char)", demangler.Parse("_ZN4funcEc"));
+  ASSERT_EQ("func(char*)", demangler.Parse("_ZN4funcEPc"));
+  ASSERT_EQ("func(char**)", demangler.Parse("_ZN4funcEPPc"));
+  ASSERT_EQ("func(char***)", demangler.Parse("_ZN4funcEPPPc"));
+  ASSERT_EQ("func(char&)", demangler.Parse("_ZN4funcERc"));
+  ASSERT_EQ("func(char*&)", demangler.Parse("_ZN4funcERPc"));
+  ASSERT_EQ("func(char&)", demangler.Parse("_ZN4funcERRc"));
+  ASSERT_EQ("func(char*&*)", demangler.Parse("_ZN4funcEPRPc"));
+  ASSERT_EQ("func(char**&)", demangler.Parse("_ZN4funcERRPPc"));
+  ASSERT_EQ("func(char const)", demangler.Parse("_ZN4funcEKc"));
+  ASSERT_EQ("func(char volatile)", demangler.Parse("_ZN4funcEVc"));
+  ASSERT_EQ("func(char volatile const)", demangler.Parse("_ZN4funcEKVc"));
+  ASSERT_EQ("func(char const volatile)", demangler.Parse("_ZN4funcEVKc"));
+  ASSERT_EQ("func(char const* volatile&)", demangler.Parse("_ZN4funcERVPKc"));
+  ASSERT_EQ("func(void, char, short)", demangler.Parse("_ZN4funcEvcs"));
+  ASSERT_EQ("func(void*, char&, short&*)", demangler.Parse("_ZN4funcEPvRcPRs"));
+}
+
+TEST(DemangleTest, FunctionModifiers) {
+  Demangler demangler;
+
+  ASSERT_EQ("func() const", demangler.Parse("_ZNK4funcEv"));
+  ASSERT_EQ("func() volatile", demangler.Parse("_ZNV4funcEv"));
+  ASSERT_EQ("func() volatile const", demangler.Parse("_ZNKV4funcEv"));
+  ASSERT_EQ("func() const volatile", demangler.Parse("_ZNVK4funcEv"));
+}
+
+TEST(DemangleTest, MultiplePartsInName) {
+  Demangler demangler;
+
+  ASSERT_EQ("one::two()", demangler.Parse("_ZN3one3twoEv"));
+  ASSERT_EQ("one::two::three()", demangler.Parse("_ZN3one3two5threeEv"));
+  ASSERT_EQ("one::two::three::four()", demangler.Parse("_ZN3one3two5three4fourEv"));
+  ASSERT_EQ("one::two::three::four::five()", demangler.Parse("_ZN3one3two5three4four4fiveEv"));
+  ASSERT_EQ("one(two::three::four::five)", demangler.Parse("_ZN3oneEN3two5three4four4fiveE"));
+}
+
+TEST(DemangleTest, AnonymousNamespace) {
+  Demangler demangler;
+
+  ASSERT_EQ("(anonymous namespace)::two()", demangler.Parse("_ZN12_GLOBAL__N_13twoEv"));
+  ASSERT_EQ("one::two((anonymous namespace))", demangler.Parse("_ZN3one3twoE12_GLOBAL__N_1"));
+}
+
+TEST(DemangleTest, DestructorValues) {
+  Demangler demangler;
+
+  ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD0Ev"));
+  ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD1Ev"));
+  ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD2Ev"));
+  ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD5Ev"));
+  ASSERT_EQ("one::two::three::~three()", demangler.Parse("_ZN3one3two5threeD0Ev"));
+
+  ASSERT_EQ("_ZN3one3twoD3Ev", demangler.Parse("_ZN3one3twoD3Ev"));
+  ASSERT_EQ("_ZN3one3twoD4Ev", demangler.Parse("_ZN3one3twoD4Ev"));
+  ASSERT_EQ("_ZN3one3twoD6Ev", demangler.Parse("_ZN3one3twoD6Ev"));
+  ASSERT_EQ("_ZN3one3twoD7Ev", demangler.Parse("_ZN3one3twoD7Ev"));
+  ASSERT_EQ("_ZN3one3twoD8Ev", demangler.Parse("_ZN3one3twoD8Ev"));
+  ASSERT_EQ("_ZN3one3twoD9Ev", demangler.Parse("_ZN3one3twoD9Ev"));
+
+  ASSERT_EQ("one::two<three::four>::~two()", demangler.Parse("_ZN3one3twoIN5three4fourEED2Ev"));
+}
+
+TEST(DemangleTest, ConstructorValues) {
+  Demangler demangler;
+
+  ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC1Ev"));
+  ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC2Ev"));
+  ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC3Ev"));
+  ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC5Ev"));
+  ASSERT_EQ("one::two::three::three()", demangler.Parse("_ZN3one3two5threeC1Ev"));
+
+  ASSERT_EQ("_ZN3one3twoC0Ev", demangler.Parse("_ZN3one3twoC0Ev"));
+  ASSERT_EQ("_ZN3one3twoC4Ev", demangler.Parse("_ZN3one3twoC4Ev"));
+  ASSERT_EQ("_ZN3one3twoC6Ev", demangler.Parse("_ZN3one3twoC6Ev"));
+  ASSERT_EQ("_ZN3one3twoC7Ev", demangler.Parse("_ZN3one3twoC7Ev"));
+  ASSERT_EQ("_ZN3one3twoC8Ev", demangler.Parse("_ZN3one3twoC8Ev"));
+  ASSERT_EQ("_ZN3one3twoC9Ev", demangler.Parse("_ZN3one3twoC9Ev"));
+
+  ASSERT_EQ("one::two<three::four>::two()", demangler.Parse("_ZN3one3twoIN5three4fourEEC1Ev"));
+}
+
+TEST(DemangleTest, OperatorValues) {
+  Demangler demangler;
+
+  ASSERT_EQ("operator&&()", demangler.Parse("_Zaav"));
+  ASSERT_EQ("operator&()", demangler.Parse("_Zadv"));
+  ASSERT_EQ("operator&()", demangler.Parse("_Zanv"));
+  ASSERT_EQ("operator&=()", demangler.Parse("_ZaNv"));
+  ASSERT_EQ("operator=()", demangler.Parse("_ZaSv"));
+  ASSERT_EQ("operator()()", demangler.Parse("_Zclv"));
+  ASSERT_EQ("operator,()", demangler.Parse("_Zcmv"));
+  ASSERT_EQ("operator~()", demangler.Parse("_Zcov"));
+  ASSERT_EQ("operator delete[]()", demangler.Parse("_Zdav"));
+  ASSERT_EQ("operator*()", demangler.Parse("_Zdev"));
+  ASSERT_EQ("operator delete()", demangler.Parse("_Zdlv"));
+  ASSERT_EQ("operator/()", demangler.Parse("_Zdvv"));
+  ASSERT_EQ("operator/=()", demangler.Parse("_ZdVv"));
+  ASSERT_EQ("operator^()", demangler.Parse("_Zeov"));
+  ASSERT_EQ("operator^=()", demangler.Parse("_ZeOv"));
+  ASSERT_EQ("operator==()", demangler.Parse("_Zeqv"));
+  ASSERT_EQ("operator>=()", demangler.Parse("_Zgev"));
+  ASSERT_EQ("operator>()", demangler.Parse("_Zgtv"));
+  ASSERT_EQ("operator[]()", demangler.Parse("_Zixv"));
+  ASSERT_EQ("operator<=()", demangler.Parse("_Zlev"));
+  ASSERT_EQ("operator<<()", demangler.Parse("_Zlsv"));
+  ASSERT_EQ("operator<<=()", demangler.Parse("_ZlSv"));
+  ASSERT_EQ("operator<()", demangler.Parse("_Zltv"));
+  ASSERT_EQ("operator-()", demangler.Parse("_Zmiv"));
+  ASSERT_EQ("operator-=()", demangler.Parse("_ZmIv"));
+  ASSERT_EQ("operator*()", demangler.Parse("_Zmlv"));
+  ASSERT_EQ("operator*=()", demangler.Parse("_ZmLv"));
+  ASSERT_EQ("operator--()", demangler.Parse("_Zmmv"));
+  ASSERT_EQ("operator new[]()", demangler.Parse("_Znav"));
+  ASSERT_EQ("operator!=()", demangler.Parse("_Znev"));
+  ASSERT_EQ("operator-()", demangler.Parse("_Zngv"));
+  ASSERT_EQ("operator!()", demangler.Parse("_Zntv"));
+  ASSERT_EQ("operator new()", demangler.Parse("_Znwv"));
+  ASSERT_EQ("operator||()", demangler.Parse("_Zoov"));
+  ASSERT_EQ("operator|()", demangler.Parse("_Zorv"));
+  ASSERT_EQ("operator|=()", demangler.Parse("_ZoRv"));
+  ASSERT_EQ("operator->*()", demangler.Parse("_Zpmv"));
+  ASSERT_EQ("operator+()", demangler.Parse("_Zplv"));
+  ASSERT_EQ("operator+=()", demangler.Parse("_ZpLv"));
+  ASSERT_EQ("operator++()", demangler.Parse("_Zppv"));
+  ASSERT_EQ("operator+()", demangler.Parse("_Zpsv"));
+  ASSERT_EQ("operator->()", demangler.Parse("_Zptv"));
+  ASSERT_EQ("operator?()", demangler.Parse("_Zquv"));
+  ASSERT_EQ("operator%()", demangler.Parse("_Zrmv"));
+  ASSERT_EQ("operator%=()", demangler.Parse("_ZrMv"));
+  ASSERT_EQ("operator>>()", demangler.Parse("_Zrsv"));
+  ASSERT_EQ("operator>>=()", demangler.Parse("_ZrSv"));
+
+  // Spot check using an operator as part of function name.
+  ASSERT_EQ("operator&&()", demangler.Parse("_ZNaaEv"));
+  ASSERT_EQ("operator++()", demangler.Parse("_ZNppEv"));
+  ASSERT_EQ("one::operator++()", demangler.Parse("_ZN3oneppEv"));
+
+  // Spot check using an operator in an argument name.
+  ASSERT_EQ("operator+(operator|=)", demangler.Parse("_ZNpsENoRE"));
+  ASSERT_EQ("operator==()", demangler.Parse("_Zeqv"));
+  ASSERT_EQ("one(arg1::operator|=, arg2::operator==)",
+            demangler.Parse("_ZN3oneEN4arg1oREN4arg2eqE"));
+}
+
+TEST(DemangleTest, FunctionStartsWithNumber) {
+  Demangler demangler;
+
+  ASSERT_EQ("value(char, int)", demangler.Parse("_Z5valueci"));
+  ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_Z11abcdefjklmna"));
+  ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
+}
+
+TEST(DemangleTest, FunctionStartsWithLPlusNumber) {
+  Demangler demangler;
+
+  ASSERT_EQ("value(char, int)", demangler.Parse("_ZL5valueci"));
+  ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_ZL11abcdefjklmna"));
+  ASSERT_EQ("value(one, signed char)", demangler.Parse("_ZL5value3onea"));
+}
+
+TEST(DemangleTest, StdTypes) {
+  Demangler demangler;
+
+  ASSERT_EQ("std::one", demangler.Parse("_ZNSt3oneE"));
+  ASSERT_EQ("std::one(std::two)", demangler.Parse("_ZNSt3oneESt3two"));
+  ASSERT_EQ("std::std::one(std::two)", demangler.Parse("_ZNStSt3oneESt3two"));
+  ASSERT_EQ("std()", demangler.Parse("_ZNStEv"));
+  ASSERT_EQ("one::std::std::two::~two(one::std::std::two)",
+            demangler.Parse("_ZN3oneStSt3twoD0ES0_"));
+
+  ASSERT_EQ("std::allocator", demangler.Parse("_ZNSaE"));
+  ASSERT_EQ("std::basic_string", demangler.Parse("_ZNSbE"));
+  ASSERT_EQ("_ZNScE", demangler.Parse("_ZNScE"));
+  ASSERT_EQ("std::iostream", demangler.Parse("_ZNSdE"));
+  ASSERT_EQ("_ZNSeE", demangler.Parse("_ZNSeE"));
+  ASSERT_EQ("_ZNSfE", demangler.Parse("_ZNSfE"));
+  ASSERT_EQ("_ZNSgE", demangler.Parse("_ZNSgE"));
+  ASSERT_EQ("_ZNShE", demangler.Parse("_ZNShE"));
+  ASSERT_EQ("std::istream", demangler.Parse("_ZNSiE"));
+  ASSERT_EQ("_ZNSjE", demangler.Parse("_ZNSjE"));
+  ASSERT_EQ("_ZNSkE", demangler.Parse("_ZNSkE"));
+  ASSERT_EQ("_ZNSlE", demangler.Parse("_ZNSlE"));
+  ASSERT_EQ("_ZNSmE", demangler.Parse("_ZNSmE"));
+  ASSERT_EQ("_ZNSnE", demangler.Parse("_ZNSnE"));
+  ASSERT_EQ("std::ostream", demangler.Parse("_ZNSoE"));
+  ASSERT_EQ("_ZNSpE", demangler.Parse("_ZNSpE"));
+  ASSERT_EQ("_ZNSqE", demangler.Parse("_ZNSqE"));
+  ASSERT_EQ("_ZNSrE", demangler.Parse("_ZNSrE"));
+  ASSERT_EQ("std::string", demangler.Parse("_ZNSsE"));
+  ASSERT_EQ("_ZNSuE", demangler.Parse("_ZNSuE"));
+  ASSERT_EQ("_ZNSvE", demangler.Parse("_ZNSvE"));
+  ASSERT_EQ("_ZNSwE", demangler.Parse("_ZNSwE"));
+  ASSERT_EQ("_ZNSxE", demangler.Parse("_ZNSxE"));
+  ASSERT_EQ("_ZNSyE", demangler.Parse("_ZNSyE"));
+  ASSERT_EQ("_ZNSzE", demangler.Parse("_ZNSzE"));
+}
+
+TEST(DemangleTest, SingleLetterArguments) {
+  Demangler demangler;
+
+  ASSERT_EQ("func(signed char)", demangler.Parse("_ZN4funcEa"));
+  ASSERT_EQ("func(bool)", demangler.Parse("_ZN4funcEb"));
+  ASSERT_EQ("func(char)", demangler.Parse("_ZN4funcEc"));
+  ASSERT_EQ("func(double)", demangler.Parse("_ZN4funcEd"));
+  ASSERT_EQ("func(long double)", demangler.Parse("_ZN4funcEe"));
+  ASSERT_EQ("func(float)", demangler.Parse("_ZN4funcEf"));
+  ASSERT_EQ("func(__float128)", demangler.Parse("_ZN4funcEg"));
+  ASSERT_EQ("func(unsigned char)", demangler.Parse("_ZN4funcEh"));
+  ASSERT_EQ("func(int)", demangler.Parse("_ZN4funcEi"));
+  ASSERT_EQ("func(unsigned int)", demangler.Parse("_ZN4funcEj"));
+  ASSERT_EQ("_ZN4funcEk", demangler.Parse("_ZN4funcEk"));
+  ASSERT_EQ("func(long)", demangler.Parse("_ZN4funcEl"));
+  ASSERT_EQ("func(unsigned long)", demangler.Parse("_ZN4funcEm"));
+  ASSERT_EQ("func(__int128)", demangler.Parse("_ZN4funcEn"));
+  ASSERT_EQ("func(unsigned __int128)", demangler.Parse("_ZN4funcEo"));
+  ASSERT_EQ("_ZN4funcEp", demangler.Parse("_ZN4funcEp"));
+  ASSERT_EQ("_ZN4funcEq", demangler.Parse("_ZN4funcEq"));
+  ASSERT_EQ("_ZN4funcEr", demangler.Parse("_ZN4funcEr"));
+  ASSERT_EQ("func(short)", demangler.Parse("_ZN4funcEs"));
+  ASSERT_EQ("func(unsigned short)", demangler.Parse("_ZN4funcEt"));
+  ASSERT_EQ("_ZN4funcEu", demangler.Parse("_ZN4funcEu"));
+  ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
+  ASSERT_EQ("func(wchar_t)", demangler.Parse("_ZN4funcEw"));
+  ASSERT_EQ("func(long long)", demangler.Parse("_ZN4funcEx"));
+  ASSERT_EQ("func(unsigned long long)", demangler.Parse("_ZN4funcEy"));
+  ASSERT_EQ("func(...)", demangler.Parse("_ZN4funcEz"));
+}
+
+TEST(DemangleTest, DArguments) {
+  Demangler demangler;
+
+  ASSERT_EQ("func(auto)", demangler.Parse("_ZN4funcEDa"));
+  ASSERT_EQ("_ZN4funcEDb", demangler.Parse("_ZN4funcEDb"));
+  ASSERT_EQ("_ZN4funcEDc", demangler.Parse("_ZN4funcEDc"));
+  ASSERT_EQ("func(decimal64)", demangler.Parse("_ZN4funcEDd"));
+  ASSERT_EQ("func(decimal128)", demangler.Parse("_ZN4funcEDe"));
+  ASSERT_EQ("func(decimal32)", demangler.Parse("_ZN4funcEDf"));
+  ASSERT_EQ("_ZN4funcEDg", demangler.Parse("_ZN4funcEDg"));
+  ASSERT_EQ("func(half)", demangler.Parse("_ZN4funcEDh"));
+  ASSERT_EQ("func(char32_t)", demangler.Parse("_ZN4funcEDi"));
+  ASSERT_EQ("_ZN4funcEDj", demangler.Parse("_ZN4funcEDj"));
+  ASSERT_EQ("_ZN4funcEDk", demangler.Parse("_ZN4funcEDk"));
+  ASSERT_EQ("_ZN4funcEDl", demangler.Parse("_ZN4funcEDl"));
+  ASSERT_EQ("_ZN4funcEDm", demangler.Parse("_ZN4funcEDm"));
+  ASSERT_EQ("func(decltype(nullptr))", demangler.Parse("_ZN4funcEDn"));
+  ASSERT_EQ("_ZN4funcEDo", demangler.Parse("_ZN4funcEDo"));
+  ASSERT_EQ("_ZN4funcEDp", demangler.Parse("_ZN4funcEDp"));
+  ASSERT_EQ("_ZN4funcEDq", demangler.Parse("_ZN4funcEDq"));
+  ASSERT_EQ("_ZN4funcEDr", demangler.Parse("_ZN4funcEDr"));
+  ASSERT_EQ("func(char16_t)", demangler.Parse("_ZN4funcEDs"));
+  ASSERT_EQ("_ZN4funcEDt", demangler.Parse("_ZN4funcEDt"));
+  ASSERT_EQ("_ZN4funcEDu", demangler.Parse("_ZN4funcEDu"));
+  ASSERT_EQ("_ZN4funcEDv", demangler.Parse("_ZN4funcEDv"));
+  ASSERT_EQ("_ZN4funcEDw", demangler.Parse("_ZN4funcEDw"));
+  ASSERT_EQ("_ZN4funcEDx", demangler.Parse("_ZN4funcEDx"));
+  ASSERT_EQ("_ZN4funcEDy", demangler.Parse("_ZN4funcEDy"));
+  ASSERT_EQ("_ZN4funcEDz", demangler.Parse("_ZN4funcEDz"));
+}
+
+TEST(DemangleTest, FunctionArguments) {
+  Demangler demangler;
+
+  ASSERT_EQ("func(char ())", demangler.Parse("_ZN4funcEFcvE"));
+  ASSERT_EQ("func(char (*)())", demangler.Parse("_ZN4funcEPFcvE"));
+  ASSERT_EQ("func(char (&)())", demangler.Parse("_ZN4funcERFcvE"));
+  ASSERT_EQ("func(char (&)())", demangler.Parse("_ZN4funcERFcvE"));
+  ASSERT_EQ("func(char (*&)())", demangler.Parse("_ZN4funcERPFcvE"));
+  ASSERT_EQ("func(char (*)(int) const)", demangler.Parse("_ZN4funcEPKFciE"));
+  ASSERT_EQ("func(char (&)() const)", demangler.Parse("_ZN4funcERKFcvE"));
+  ASSERT_EQ("func(char (&)() volatile)", demangler.Parse("_ZN4funcERVFcvE"));
+  ASSERT_EQ("func(char (&)() volatile const)", demangler.Parse("_ZN4funcERKVFcvE"));
+  ASSERT_EQ("func(char (&)() const volatile)", demangler.Parse("_ZN4funcERVKFcvE"));
+  ASSERT_EQ("func(char (&)(int, signed char) const)", demangler.Parse("_ZN4funcERKFciaE"));
+  ASSERT_EQ("fake(char (&* volatile const)(void, void, signed char), signed char)",
+            demangler.Parse("_ZN4fakeEKVPRFcvvaEa"));
+}
+
+TEST(DemangleTest, TemplateFunction) {
+  Demangler demangler;
+
+  ASSERT_EQ("one<char>", demangler.Parse("_ZN3oneIcEE"));
+  ASSERT_EQ("one<void>", demangler.Parse("_ZN3oneIvEE"));
+  ASSERT_EQ("one<void*>", demangler.Parse("_ZN3oneIPvEE"));
+  ASSERT_EQ("one<void const>", demangler.Parse("_ZN3oneIKvEE"));
+  ASSERT_EQ("one<char, int, bool>", demangler.Parse("_ZN3oneIcibEE"));
+  ASSERT_EQ("one::two<three>", demangler.Parse("_ZN3one3twoIN5threeEEE"));
+  ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_ZN3oneIciN3two5threeEEE"));
+  // Template within templates.
+  ASSERT_EQ("one::two<three<char, int>>", demangler.Parse("_ZN3one3twoIN5threeIciEEEE"));
+  ASSERT_EQ("one::two<three<char, four<int>>>", demangler.Parse("_ZN3one3twoIN5threeIcN4fourIiEEEEEE"));
+
+  ASSERT_EQ("one<char>", demangler.Parse("_Z3oneIcE"));
+  ASSERT_EQ("one<void>", demangler.Parse("_Z3oneIvE"));
+  ASSERT_EQ("one<void*>", demangler.Parse("_Z3oneIPvE"));
+  ASSERT_EQ("one<void const>", demangler.Parse("_Z3oneIKvE"));
+  ASSERT_EQ("one<char, int, bool>", demangler.Parse("_Z3oneIcibE"));
+  ASSERT_EQ("one(two<three>)", demangler.Parse("_Z3one3twoIN5threeEE"));
+  ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_Z3oneIciN3two5threeEE"));
+  // Template within templates.
+  ASSERT_EQ("one(two<three<char, int>>)", demangler.Parse("_Z3one3twoIN5threeIciEEE"));
+  ASSERT_EQ("one(two<three<char, four<int>>>)",
+            demangler.Parse("_Z3one3twoIN5threeIcN4fourIiEEEEE"));
+}
+
+TEST(DemangleTest, TemplateFunctionWithReturnType) {
+  Demangler demangler;
+
+  ASSERT_EQ("char one<int>(char)", demangler.Parse("_Z3oneIiEcc"));
+  ASSERT_EQ("void one<int>()", demangler.Parse("_Z3oneIiEvv"));
+  ASSERT_EQ("char one<int>()", demangler.Parse("_Z3oneIiEcv"));
+  ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_Z3oneIiEcvv"));
+  ASSERT_EQ("char one<int>()", demangler.Parse("_ZN3oneIiEEcv"));
+  ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_ZN3oneIiEEcvv"));
+}
+
+TEST(DemangleTest, TemplateArguments) {
+  Demangler demangler;
+
+  ASSERT_EQ("one(two<char>)", demangler.Parse("_ZN3oneE3twoIcE"));
+  ASSERT_EQ("one(two<char, void>)", demangler.Parse("_ZN3oneE3twoIcvE"));
+  ASSERT_EQ("one(two<char, void, three<four, int>>)",
+            demangler.Parse("_ZN3oneE3twoIcv5threeI4fouriEE"));
+}
+
+TEST(DemangleTest, SubstitutionUnderscore) {
+  Demangler demangler;
+
+  ASSERT_EQ("a::a", demangler.Parse("_ZN1aS_E"));
+  ASSERT_EQ("one::one", demangler.Parse("_ZN3oneS_E"));
+  ASSERT_EQ("one::two::one", demangler.Parse("_ZN3one3twoS_E"));
+  ASSERT_EQ("one::two::three::one", demangler.Parse("_ZN3one3two5threeS_E"));
+  ASSERT_EQ("one::two(one)", demangler.Parse("_ZN3one3twoES_"));
+  ASSERT_EQ("one::two(three::one)", demangler.Parse("_ZN3one3twoEN5threeS_E"));
+
+  // Special case that St is part of the saved value used in the substitution.
+  ASSERT_EQ("std::one::std::one", demangler.Parse("_ZNSt3oneS_E"));
+
+  // Multiple substitutions in the string.
+  ASSERT_EQ("one::one(one, one)", demangler.Parse("_ZN3oneS_ES_S_"));
+  ASSERT_EQ("std::one::two::std::one(std::one)", demangler.Parse("_ZNSt3one3twoS_ES_"));
+}
+
+TEST(DemangleTest, SubstitutionByNumber) {
+  Demangler demangler;
+
+  // Basic substitution.
+  ASSERT_EQ("a::b::c(a::b)", demangler.Parse("_ZN1a1b1cES0_"));
+  ASSERT_EQ("_ZN1a1b1cES1_", demangler.Parse("_ZN1a1b1cES1_"));
+  ASSERT_EQ("a::b::c::d(a::b::c)", demangler.Parse("_ZN1a1b1c1dES1_"));
+  ASSERT_EQ("a::b::c::d::e::f::g::h::i::j::k::l::m::n::o::p::q(a::b::c::d::e::f::g::h::i::j::k::l)",
+            demangler.Parse("_ZN1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1qESA_"));
+  ASSERT_EQ("a::b::c::d::e::f::g::h::i::j::k::l::m::n::o::p::q(a::b::c::d::e::f::g::h::i::j::k::l::m)",
+            demangler.Parse("_ZN1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1qESB_"));
+
+  // Verify argument modifiers are included in substitution list.
+  ASSERT_EQ("one::two(char&* volatile const, char&)", demangler.Parse("_ZN3one3twoEKVPRcS0_"));
+  ASSERT_EQ("one::two(char&* volatile const, char&*)", demangler.Parse("_ZN3one3twoEKVPRcS1_"));
+  ASSERT_EQ("one::two(char&* volatile const, char&* volatile const)",
+            demangler.Parse("_ZN3one3twoEKVPRcS2_"));
+  ASSERT_EQ("one::two(int&* volatile* const, int&)", demangler.Parse("_ZN3one3twoEKPVPRiS0_"));
+  ASSERT_EQ("one::two(int&* volatile const, int&*)", demangler.Parse("_ZN3one3twoEKVPRiS1_"));
+  ASSERT_EQ("one::two(int&* volatile const, int&* volatile const)",
+            demangler.Parse("_ZN3one3twoEKVPRiS2_"));
+
+  // Verify Constructor/Destructor does properly save from function name.
+  ASSERT_EQ("_ZN1a1bES0_", demangler.Parse("_ZN1a1bES0_"));
+  ASSERT_EQ("a::b::b(a::b)", demangler.Parse("_ZN1a1bC1ES0_"));
+  ASSERT_EQ("a::b::~b(a::b)", demangler.Parse("_ZN1a1bD0ES0_"));
+
+  // Make sure substitution values are not saved.
+  ASSERT_EQ("a::b::b(a::b, char*, char*)", demangler.Parse("_ZN1a1bC1ES0_PcS1_"));
+}
+
+TEST(DemangleTest, ComplexSubstitution) {
+  Demangler demangler;
+
+  ASSERT_EQ("one::two<one::three>::two()", demangler.Parse("_ZN3one3twoINS_5threeEEC1Ev"));
+  ASSERT_EQ("one::two::two(one::two const&, bool, one::three*)",
+            demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE"));
+  ASSERT_EQ("one::two::three::four<one::five>::~four(one::two*)",
+            demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS0_"));
+  ASSERT_EQ("one::two::three::four<one::five>::~four(one::two::three*)",
+            demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS1_"));
+  ASSERT_EQ("one::two::three::four<one::five>::~four(one::two::three::four*)",
+            demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS2_"));
+  ASSERT_EQ("one::two::three::four<one::five>::~four(one::five*)",
+            demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS3_"));
+}
+
+TEST(DemangleTest, TemplateSubstitution) {
+  Demangler demangler;
+
+  ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_ZN3oneIidEEvT_"));
+  ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_ZN3oneIidEEvT0_"));
+  ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_ZN3oneIidcvEEvT1_"));
+
+  ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_Z3oneIidEvT_"));
+  ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_Z3oneIidEvT0_"));
+  ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_Z3oneIidcvEvT1_"));
+
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
+            demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT10_"));
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
+            demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT11_"));
+
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
+            demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT10_"));
+  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
+            demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT11_"));
+}
+
+TEST(DemangleTest, StringTooLong) {
+  Demangler demangler;
+
+  ASSERT_EQ("_ZN3one3twoC2ERKS0_bPNS_5threeE",
+            demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 10));
+  ASSERT_EQ("_ZN3one3twoC2ERKS0_bPNS_5threeE",
+            demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 30));
+  ASSERT_EQ("one::two::two(one::two const&, bool, one::three*)",
+            demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 31));
+
+  // Check the length check only occurs after the two letter value
+  // has been processed.
+  ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 15));
+  ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 14));
+  ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 13));
+  ASSERT_EQ("_ZN3one3twoEDa", demangler.Parse("_ZN3one3twoEDa", 12));
+}
+
+TEST(DemangleTest, BooleanLiterals) {
+  Demangler demangler;
+
+  ASSERT_EQ("one<true>", demangler.Parse("_ZN3oneILb1EEE"));
+  ASSERT_EQ("one<false>", demangler.Parse("_ZN3oneILb0EEE"));
+  ASSERT_EQ("one<false, true>", demangler.Parse("_ZN3oneILb0ELb1EEE"));
+
+  ASSERT_EQ("one<true>", demangler.Parse("_Z3oneILb1EE"));
+  ASSERT_EQ("one<false>", demangler.Parse("_Z3oneILb0EE"));
+  ASSERT_EQ("one<false, true>", demangler.Parse("_Z3oneILb0ELb1EE"));
+
+  ASSERT_EQ("one(two<three<four>, false, true>)",
+            demangler.Parse("_ZN3oneE3twoI5threeI4fourELb0ELb1EE"));
+}
+
+TEST(DemangleTest, non_virtual_thunk) {
+  Demangler demangler;
+
+  ASSERT_EQ("non-virtual thunk to one", demangler.Parse("_ZThn0_N3oneE"));
+  ASSERT_EQ("non-virtual thunk to two", demangler.Parse("_ZThn0_3two"));
+  ASSERT_EQ("non-virtual thunk to three", demangler.Parse("_ZTh0_5three"));
+  ASSERT_EQ("non-virtual thunk to four", demangler.Parse("_ZTh_4four"));
+  ASSERT_EQ("non-virtual thunk to five", demangler.Parse("_ZTh0123456789_4five"));
+  ASSERT_EQ("non-virtual thunk to six", demangler.Parse("_ZThn0123456789_3six"));
+
+  ASSERT_EQ("_ZThn0N3oneE", demangler.Parse("_ZThn0N3oneE"));
+  ASSERT_EQ("_ZThn03two", demangler.Parse("_ZThn03two"));
+  ASSERT_EQ("_ZTh05three", demangler.Parse("_ZTh05three"));
+  ASSERT_EQ("_ZTh4four", demangler.Parse("_ZTh4four"));
+  ASSERT_EQ("_ZTh01234567894five", demangler.Parse("_ZTh01234567894five"));
+  ASSERT_EQ("_ZThn01234567893six", demangler.Parse("_ZThn01234567893six"));
+  ASSERT_EQ("_ZT_N3oneE", demangler.Parse("_ZT_N3oneE"));
+  ASSERT_EQ("_ZT0_N3oneE", demangler.Parse("_ZT0_N3oneE"));
+  ASSERT_EQ("_ZTH_N3oneE", demangler.Parse("_ZTH_N3oneE"));
+}
+
+TEST(DemangleTest, r_value_reference) {
+  Demangler demangler;
+  ASSERT_EQ(
+      "android::SurfaceComposerClient::Transaction::merge(android::SurfaceComposerClient::"
+      "Transaction&&)",
+      demangler.Parse("_ZN7android21SurfaceComposerClient11Transaction5mergeEOS1_"));
+}
+
+TEST(DemangleTest, initial_St) {
+  Demangler demangler;
+  EXPECT_EQ("std::state", demangler.Parse("_ZSt5state"));
+  EXPECT_EQ("std::_In::ward", demangler.Parse("_ZNSt3_In4wardE"));
+  EXPECT_EQ("std::__terminate(void (*)())", demangler.Parse("_ZSt11__terminatePFvvE"));
+}
+
+TEST(DemangleTest, cfi) {
+  Demangler demangler;
+  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*)",
+            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB"));
+  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*) [clone .cfi]",
+            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB.cfi"));
+}
+
+TEST(DemangleTest, demangle) {
+  std::string str;
+
+  str = demangle("_ZN1a1b1cES0_");
+  ASSERT_EQ("a::b::c(a::b)", str);
+
+  str = demangle("_");
+  ASSERT_EQ("_", str);
+
+  str = demangle("_Z");
+  ASSERT_EQ("_Z", str);
+
+  str = demangle("_Za");
+  ASSERT_EQ("_Za", str);
+
+  str = demangle("_Zaa");
+  ASSERT_EQ("operator&&", str);
+
+  str = demangle("Xa");
+  ASSERT_EQ("Xa", str);
+}
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
new file mode 100644
index 0000000..7a3aa81
--- /dev/null
+++ b/demangle/Demangler.cpp
@@ -0,0 +1,924 @@
+/*
+ * 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 <assert.h>
+
+#include <cctype>
+#include <stack>
+#include <string>
+#include <vector>
+
+#include "Demangler.h"
+
+constexpr const char* Demangler::kTypes[];
+constexpr const char* Demangler::kDTypes[];
+constexpr const char* Demangler::kSTypes[];
+
+void Demangler::Save(const std::string& str, bool is_name) {
+  saves_.push_back(str);
+  last_save_name_ = is_name;
+}
+
+std::string Demangler::GetArgumentsString() {
+  size_t num_args = cur_state_.args.size();
+  std::string arg_str;
+  if (num_args > 0) {
+    arg_str = cur_state_.args[0];
+    for (size_t i = 1; i < num_args; i++) {
+      arg_str += ", " + cur_state_.args[i];
+    }
+  }
+  return arg_str;
+}
+
+const char* Demangler::AppendOperatorString(const char* name) {
+  const char* oper = nullptr;
+  switch (*name) {
+  case 'a':
+    name++;
+    switch (*name) {
+    case 'a':
+      oper = "operator&&";
+      break;
+    case 'd':
+    case 'n':
+      oper = "operator&";
+      break;
+    case 'N':
+      oper = "operator&=";
+      break;
+    case 'S':
+      oper = "operator=";
+      break;
+    }
+    break;
+  case 'c':
+    name++;
+    switch (*name) {
+    case 'l':
+      oper = "operator()";
+      break;
+    case 'm':
+      oper = "operator,";
+      break;
+    case 'o':
+      oper = "operator~";
+      break;
+    }
+    break;
+  case 'd':
+    name++;
+    switch (*name) {
+    case 'a':
+      oper = "operator delete[]";
+      break;
+    case 'e':
+      oper = "operator*";
+      break;
+    case 'l':
+      oper = "operator delete";
+      break;
+    case 'v':
+      oper = "operator/";
+      break;
+    case 'V':
+      oper = "operator/=";
+      break;
+    }
+    break;
+  case 'e':
+    name++;
+    switch (*name) {
+    case 'o':
+      oper = "operator^";
+      break;
+    case 'O':
+      oper = "operator^=";
+      break;
+    case 'q':
+      oper = "operator==";
+      break;
+    }
+    break;
+  case 'g':
+    name++;
+    switch (*name) {
+    case 'e':
+      oper = "operator>=";
+      break;
+    case 't':
+      oper = "operator>";
+      break;
+    }
+    break;
+  case 'i':
+    name++;
+    switch (*name) {
+    case 'x':
+      oper = "operator[]";
+      break;
+    }
+    break;
+  case 'l':
+    name++;
+    switch (*name) {
+    case 'e':
+      oper = "operator<=";
+      break;
+    case 's':
+      oper = "operator<<";
+      break;
+    case 'S':
+      oper = "operator<<=";
+      break;
+    case 't':
+      oper = "operator<";
+      break;
+    }
+    break;
+  case 'm':
+    name++;
+    switch (*name) {
+    case 'i':
+      oper = "operator-";
+      break;
+    case 'I':
+      oper = "operator-=";
+      break;
+    case 'l':
+      oper = "operator*";
+      break;
+    case 'L':
+      oper = "operator*=";
+      break;
+    case 'm':
+      oper = "operator--";
+      break;
+    }
+    break;
+  case 'n':
+    name++;
+    switch (*name) {
+    case 'a':
+      oper = "operator new[]";
+      break;
+    case 'e':
+      oper = "operator!=";
+      break;
+    case 'g':
+      oper = "operator-";
+      break;
+    case 't':
+      oper = "operator!";
+      break;
+    case 'w':
+      oper = "operator new";
+      break;
+    }
+    break;
+  case 'o':
+    name++;
+    switch (*name) {
+    case 'o':
+      oper = "operator||";
+      break;
+    case 'r':
+      oper = "operator|";
+      break;
+    case 'R':
+      oper = "operator|=";
+      break;
+    }
+    break;
+  case 'p':
+    name++;
+    switch (*name) {
+    case 'm':
+      oper = "operator->*";
+      break;
+    case 'l':
+      oper = "operator+";
+      break;
+    case 'L':
+      oper = "operator+=";
+      break;
+    case 'p':
+      oper = "operator++";
+      break;
+    case 's':
+      oper = "operator+";
+      break;
+    case 't':
+      oper = "operator->";
+      break;
+    }
+    break;
+  case 'q':
+    name++;
+    switch (*name) {
+    case 'u':
+      oper = "operator?";
+      break;
+    }
+    break;
+  case 'r':
+    name++;
+    switch (*name) {
+    case 'm':
+      oper = "operator%";
+      break;
+    case 'M':
+      oper = "operator%=";
+      break;
+    case 's':
+      oper = "operator>>";
+      break;
+    case 'S':
+      oper = "operator>>=";
+      break;
+    }
+    break;
+  }
+  if (oper == nullptr) {
+    return nullptr;
+  }
+  AppendCurrent(oper);
+  cur_state_.last_save = oper;
+  return name + 1;
+}
+
+const char* Demangler::GetStringFromLength(const char* name, std::string* str) {
+  assert(std::isdigit(*name));
+
+  size_t length = *name - '0';
+  name++;
+  while (*name != '\0' && std::isdigit(*name)) {
+    length = length * 10 + *name - '0';
+    name++;
+  }
+
+  std::string read_str;
+  while (*name != '\0' && length != 0) {
+    read_str += *name;
+    name++;
+    length--;
+  }
+  if (length != 0) {
+    return nullptr;
+  }
+  // Special replacement of _GLOBAL__N_1 to (anonymous namespace).
+  if (read_str == "_GLOBAL__N_1") {
+    *str += "(anonymous namespace)";
+  } else {
+    *str += read_str;
+  }
+  return name;
+}
+
+void Demangler::AppendCurrent(const std::string& str) {
+  if (!cur_state_.str.empty()) {
+    cur_state_.str += "::";
+  }
+  cur_state_.str += str;
+}
+
+void Demangler::AppendCurrent(const char* str) {
+  if (!cur_state_.str.empty()) {
+    cur_state_.str += "::";
+  }
+  cur_state_.str += str;
+}
+
+const char* Demangler::ParseS(const char* name) {
+  if (std::islower(*name)) {
+    const char* type = kSTypes[*name - 'a'];
+    if (type == nullptr) {
+      return nullptr;
+    }
+    AppendCurrent(type);
+    return name + 1;
+  }
+
+  if (saves_.empty()) {
+    return nullptr;
+  }
+
+  if (*name == '_') {
+    last_save_name_ = false;
+    AppendCurrent(saves_[0]);
+    return name + 1;
+  }
+
+  bool isdigit = std::isdigit(*name);
+  if (!isdigit && !std::isupper(*name)) {
+    return nullptr;
+  }
+
+  size_t index;
+  if (isdigit) {
+    index = *name - '0' + 1;
+  } else {
+    index = *name - 'A' + 11;
+  }
+  name++;
+  if (*name != '_') {
+    return nullptr;
+  }
+
+  if (index >= saves_.size()) {
+    return nullptr;
+  }
+
+  last_save_name_ = false;
+  AppendCurrent(saves_[index]);
+  return name + 1;
+}
+
+const char* Demangler::ParseT(const char* name) {
+  if (template_saves_.empty()) {
+    return nullptr;
+  }
+
+  if (*name == '_') {
+    last_save_name_ = false;
+    AppendCurrent(template_saves_[0]);
+    return name + 1;
+  }
+
+  // Need to get the total number.
+  char* end;
+  unsigned long int index = strtoul(name, &end, 10) + 1;
+  if (name == end || *end != '_') {
+    return nullptr;
+  }
+
+  if (index >= template_saves_.size()) {
+    return nullptr;
+  }
+
+  last_save_name_ = false;
+  AppendCurrent(template_saves_[index]);
+  return end + 1;
+}
+
+const char* Demangler::ParseFunctionName(const char* name) {
+  if (*name == 'E') {
+    if (parse_funcs_.empty()) {
+      return nullptr;
+    }
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+
+    // Remove the last saved part so that the full function name is not saved.
+    // But only if the last save was not something like a substitution.
+    if (!saves_.empty() && last_save_name_) {
+      saves_.pop_back();
+    }
+
+    function_name_ += cur_state_.str;
+    while (!cur_state_.suffixes.empty()) {
+      function_suffix_ += cur_state_.suffixes.back();
+      cur_state_.suffixes.pop_back();
+    }
+    cur_state_.Clear();
+
+    return name + 1;
+  }
+
+  if (*name == 'I') {
+    state_stack_.push(cur_state_);
+    cur_state_.Clear();
+
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseFunctionNameTemplate;
+    return name + 1;
+  }
+
+  return ParseComplexString(name);
+}
+
+const char* Demangler::ParseFunctionNameTemplate(const char* name) {
+  if (*name == 'E' && name[1] == 'E') {
+    // Only consider this a template with saves if it is right before
+    // the end of the name.
+    template_found_ = true;
+    template_saves_ = cur_state_.args;
+  }
+  return ParseTemplateArgumentsComplex(name);
+}
+
+const char* Demangler::ParseComplexArgument(const char* name) {
+  if (*name == 'E') {
+    if (parse_funcs_.empty()) {
+      return nullptr;
+    }
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+
+    AppendArgument(cur_state_.str);
+    cur_state_.str.clear();
+
+    return name + 1;
+  }
+
+  return ParseComplexString(name);
+}
+
+void Demangler::FinalizeTemplate() {
+  std::string arg_str(GetArgumentsString());
+  cur_state_ = state_stack_.top();
+  state_stack_.pop();
+  cur_state_.str += '<' + arg_str + '>';
+}
+
+const char* Demangler::ParseComplexString(const char* name) {
+  if (*name == 'S') {
+    name++;
+    if (*name == 't') {
+      AppendCurrent("std");
+      return name + 1;
+    }
+    return ParseS(name);
+  }
+  if (*name == 'L') {
+    name++;
+    if (!std::isdigit(*name)) {
+      return nullptr;
+    }
+  }
+  if (std::isdigit(*name)) {
+    std::string str;
+    name = GetStringFromLength(name, &str);
+    if (name == nullptr) {
+      return name;
+    }
+    AppendCurrent(str);
+    Save(cur_state_.str, true);
+    cur_state_.last_save = std::move(str);
+    return name;
+  }
+  if (*name == 'D') {
+    name++;
+    if (saves_.empty() || (*name != '0' && *name != '1' && *name != '2'
+        && *name != '5')) {
+      return nullptr;
+    }
+    last_save_name_ = false;
+    AppendCurrent("~" + cur_state_.last_save);
+    return name + 1;
+  }
+  if (*name == 'C') {
+    name++;
+    if (saves_.empty() || (*name != '1' && *name != '2' && *name != '3'
+        && *name != '5')) {
+      return nullptr;
+    }
+    last_save_name_ = false;
+    AppendCurrent(cur_state_.last_save);
+    return name + 1;
+  }
+  if (*name == 'K') {
+    cur_state_.suffixes.push_back(" const");
+    return name + 1;
+  }
+  if (*name == 'V') {
+    cur_state_.suffixes.push_back(" volatile");
+    return name + 1;
+  }
+  if (*name == 'I') {
+    // Save the current argument state.
+    state_stack_.push(cur_state_);
+    cur_state_.Clear();
+
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseTemplateArgumentsComplex;
+    return name + 1;
+  }
+  name = AppendOperatorString(name);
+  if (name != nullptr) {
+    Save(cur_state_.str, true);
+  }
+  return name;
+}
+
+void Demangler::AppendArgument(const std::string& str) {
+  std::string arg(str);
+  while (!cur_state_.suffixes.empty()) {
+    arg += cur_state_.suffixes.back();
+    cur_state_.suffixes.pop_back();
+    Save(arg, false);
+  }
+  cur_state_.args.push_back(arg);
+}
+
+const char* Demangler::ParseFunctionArgument(const char* name) {
+  if (*name == 'E') {
+    // The first argument is the function modifier.
+    // The second argument is the function type.
+    // The third argument is the return type of the function.
+    // The rest of the arguments are the function arguments.
+    size_t num_args = cur_state_.args.size();
+    if (num_args < 4) {
+      return nullptr;
+    }
+    std::string function_modifier = cur_state_.args[0];
+    std::string function_type = cur_state_.args[1];
+
+    std::string str = cur_state_.args[2] + ' ';
+    if (!cur_state_.args[1].empty()) {
+      str += '(' + cur_state_.args[1] + ')';
+    }
+
+    if (num_args == 4 && cur_state_.args[3] == "void") {
+      str += "()";
+    } else {
+      str += '(' + cur_state_.args[3];
+      for (size_t i = 4; i < num_args; i++) {
+        str += ", " + cur_state_.args[i];
+      }
+      str += ')';
+    }
+    str += cur_state_.args[0];
+
+    cur_state_ = state_stack_.top();
+    state_stack_.pop();
+    cur_state_.args.emplace_back(std::move(str));
+
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+    return name + 1;
+  }
+  return ParseArguments(name);
+}
+
+const char* Demangler::ParseArguments(const char* name) {
+  switch (*name) {
+  case 'P':
+    cur_state_.suffixes.push_back("*");
+    return name + 1;
+
+  case 'R':
+    // This should always be okay because the string is guaranteed to have
+    // at least two characters before this. A mangled string always starts
+    // with _Z.
+    if (name[-1] != 'R') {
+      // Multiple 'R's in a row only add a single &.
+      cur_state_.suffixes.push_back("&");
+    }
+    return name + 1;
+
+  case 'O':
+    cur_state_.suffixes.push_back("&&");
+    return name + 1;
+
+  case 'K':
+  case 'V': {
+    const char* suffix;
+    if (*name == 'K') {
+      suffix = " const";
+    } else {
+      suffix = " volatile";
+    }
+    if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) {
+      // Special case, const/volatile apply as a single entity.
+      size_t index = cur_state_.suffixes.size();
+      cur_state_.suffixes[index-1].insert(0, suffix);
+    } else {
+      cur_state_.suffixes.push_back(suffix);
+    }
+    return name + 1;
+  }
+
+  case 'F': {
+    std::string function_modifier;
+    std::string function_type;
+    if (!cur_state_.suffixes.empty()) {
+      // If the first element starts with a ' ', then this modifies the
+      // function itself.
+      if (cur_state_.suffixes.back()[0] == ' ') {
+        function_modifier = cur_state_.suffixes.back();
+        cur_state_.suffixes.pop_back();
+      }
+      while (!cur_state_.suffixes.empty()) {
+        function_type += cur_state_.suffixes.back();
+        cur_state_.suffixes.pop_back();
+      }
+    }
+
+    state_stack_.push(cur_state_);
+
+    cur_state_.Clear();
+
+    // The function parameter has this format:
+    //   First argument is the function modifier.
+    //   Second argument is the function type.
+    //   Third argument will be the return function type but has not
+    //     been parsed yet.
+    //   Any other parameters are the arguments to the function. There
+    //     must be at least one or this isn't valid.
+    cur_state_.args.push_back(function_modifier);
+    cur_state_.args.push_back(function_type);
+
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseFunctionArgument;
+    return name + 1;
+  }
+
+  case 'N':
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseComplexArgument;
+    return name + 1;
+
+  case 'S':
+    name++;
+    if (*name == 't') {
+      cur_state_.str = "std::";
+      return name + 1;
+    }
+    name = ParseS(name);
+    if (name == nullptr) {
+      return nullptr;
+    }
+    AppendArgument(cur_state_.str);
+    cur_state_.str.clear();
+    return name;
+
+  case 'D':
+    name++;
+    if (*name >= 'a' && *name <= 'z') {
+      const char* arg = Demangler::kDTypes[*name - 'a'];
+      if (arg == nullptr) {
+        return nullptr;
+      }
+      AppendArgument(arg);
+      return name + 1;
+    }
+    return nullptr;
+
+  case 'I':
+    // Save the current argument state.
+    state_stack_.push(cur_state_);
+    cur_state_.Clear();
+
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseTemplateArguments;
+    return name + 1;
+
+  case 'v':
+    AppendArgument("void");
+    return name + 1;
+
+  default:
+    if (*name >= 'a' && *name <= 'z') {
+      const char* arg = Demangler::kTypes[*name - 'a'];
+      if (arg == nullptr) {
+        return nullptr;
+      }
+      AppendArgument(arg);
+      return name + 1;
+    } else if (std::isdigit(*name)) {
+      std::string arg = cur_state_.str;
+      name = GetStringFromLength(name, &arg);
+      if (name == nullptr) {
+        return nullptr;
+      }
+      Save(arg, true);
+      if (*name == 'I') {
+        // There is one case where this argument is not complete, and that's
+        // where this is a template argument.
+        cur_state_.str = arg;
+      } else {
+        AppendArgument(arg);
+        cur_state_.str.clear();
+      }
+      return name;
+    } else if (strcmp(name, ".cfi") == 0) {
+      function_suffix_ += " [clone .cfi]";
+      return name + 4;
+    }
+  }
+  return nullptr;
+}
+
+const char* Demangler::ParseTemplateLiteral(const char* name) {
+  if (*name == 'E') {
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+    return name + 1;
+  }
+  // Only understand boolean values with 0 or 1.
+  if (*name == 'b') {
+    name++;
+    if (*name == '0') {
+      AppendArgument("false");
+      cur_state_.str.clear();
+    } else if (*name == '1') {
+      AppendArgument("true");
+      cur_state_.str.clear();
+    } else {
+      return nullptr;
+    }
+    return name + 1;
+  }
+  return nullptr;
+}
+
+const char* Demangler::ParseTemplateArgumentsComplex(const char* name) {
+  if (*name == 'E') {
+    if (parse_funcs_.empty()) {
+      return nullptr;
+    }
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+
+    FinalizeTemplate();
+    Save(cur_state_.str, false);
+    return name + 1;
+  } else if (*name == 'L') {
+    // Literal value for a template.
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseTemplateLiteral;
+    return name + 1;
+  }
+
+  return ParseArguments(name);
+}
+
+const char* Demangler::ParseTemplateArguments(const char* name) {
+  if (*name == 'E') {
+    if (parse_funcs_.empty()) {
+      return nullptr;
+    }
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+    FinalizeTemplate();
+    AppendArgument(cur_state_.str);
+    cur_state_.str.clear();
+    return name + 1;
+  } else if (*name == 'L') {
+    // Literal value for a template.
+    parse_funcs_.push_back(parse_func_);
+    parse_func_ = &Demangler::ParseTemplateLiteral;
+    return name + 1;
+  }
+
+  return ParseArguments(name);
+}
+
+const char* Demangler::ParseFunctionTemplateArguments(const char* name) {
+  if (*name == 'E') {
+    parse_func_ = parse_funcs_.back();
+    parse_funcs_.pop_back();
+
+    function_name_ += '<' + GetArgumentsString() + '>';
+    template_found_ = true;
+    template_saves_ = cur_state_.args;
+    cur_state_.Clear();
+    return name + 1;
+  }
+  return ParseTemplateArgumentsComplex(name);
+}
+
+const char* Demangler::FindFunctionName(const char* name) {
+  if (*name == 'T') {
+    // non-virtual thunk, verify that it matches one of these patterns:
+    //   Thn[0-9]+_
+    //   Th[0-9]+_
+    //   Thn_
+    //   Th_
+    name++;
+    if (*name != 'h') {
+      return nullptr;
+    }
+    name++;
+    if (*name == 'n') {
+      name++;
+    }
+    while (std::isdigit(*name)) {
+      name++;
+    }
+    if (*name != '_') {
+      return nullptr;
+    }
+    function_name_ = "non-virtual thunk to ";
+    return name + 1;
+  }
+
+  if (*name == 'N') {
+    parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
+    parse_func_ = &Demangler::ParseFunctionName;
+    return name + 1;
+  }
+
+  if (*name == 'S') {
+    name++;
+    if (*name == 't') {
+      function_name_ = "std::";
+      name++;
+    } else {
+      return nullptr;
+    }
+  }
+
+  if (std::isdigit(*name)) {
+    name = GetStringFromLength(name, &function_name_);
+  } else if (*name == 'L' && std::isdigit(name[1])) {
+    name = GetStringFromLength(name + 1, &function_name_);
+  } else {
+    name = AppendOperatorString(name);
+    function_name_ = cur_state_.str;
+  }
+  cur_state_.Clear();
+
+  // Check for a template argument, which will still be part of the function
+  // name.
+  if (name != nullptr && *name == 'I') {
+    parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
+    parse_func_ = &Demangler::ParseFunctionTemplateArguments;
+    return name + 1;
+  }
+  parse_func_ = &Demangler::ParseArgumentsAtTopLevel;
+  return name;
+}
+
+const char* Demangler::ParseArgumentsAtTopLevel(const char* name) {
+  // At the top level is the only place where T is allowed.
+  if (*name == 'T') {
+    name++;
+    name = ParseT(name);
+    if (name == nullptr) {
+      return nullptr;
+    }
+    AppendArgument(cur_state_.str);
+    cur_state_.str.clear();
+    return name;
+  }
+
+  return Demangler::ParseArguments(name);
+}
+
+std::string Demangler::Parse(const char* name, size_t max_length) {
+  if (name[0] == '\0' || name[0] != '_' || name[1] == '\0' || name[1] != 'Z') {
+    // Name is not mangled.
+    return name;
+  }
+
+  Clear();
+
+  parse_func_ = &Demangler::FindFunctionName;
+  parse_funcs_.push_back(&Demangler::Fail);
+  const char* cur_name = name + 2;
+  while (cur_name != nullptr && *cur_name != '\0'
+      && static_cast<size_t>(cur_name - name) < max_length) {
+    cur_name = (this->*parse_func_)(cur_name);
+  }
+  if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() ||
+      !cur_state_.suffixes.empty()) {
+    return name;
+  }
+
+  std::string return_type;
+  if (template_found_) {
+    // Only a single argument with a template is not allowed.
+    if (cur_state_.args.size() == 1) {
+      return name;
+    }
+
+    // If there are at least two arguments, this template has a return type.
+    if (cur_state_.args.size() > 1) {
+      // The first argument will be the return value.
+      return_type = cur_state_.args[0] + ' ';
+      cur_state_.args.erase(cur_state_.args.begin());
+    }
+  }
+
+  std::string arg_str;
+  if (cur_state_.args.size() == 1 && cur_state_.args[0] == "void") {
+    // If the only argument is void, then don't print any args.
+    arg_str = "()";
+  } else {
+    arg_str = GetArgumentsString();
+    if (!arg_str.empty()) {
+      arg_str = '(' + arg_str + ')';
+    }
+  }
+  return return_type + function_name_ + arg_str + function_suffix_;
+}
+
+std::string demangle(const char* name) {
+  Demangler demangler;
+  return demangler.Parse(name);
+}
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
new file mode 100644
index 0000000..3b7d44e
--- /dev/null
+++ b/demangle/Demangler.h
@@ -0,0 +1,200 @@
+/*
+ * 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 __LIB_DEMANGLE_DEMANGLER_H
+#define __LIB_DEMANGLE_DEMANGLER_H
+
+#include <assert.h>
+
+#include <stack>
+#include <string>
+#include <vector>
+
+class Demangler {
+ public:
+  Demangler() = default;
+
+  // NOTE: The max_length is not guaranteed to be the absolute max length
+  // of a string that will be rejected. Under certain circumstances the
+  // length check will not occur until after the second letter of a pair
+  // is checked.
+  std::string Parse(const char* name, size_t max_length = kMaxDefaultLength);
+
+  void AppendCurrent(const std::string& str);
+  void AppendCurrent(const char* str);
+  void AppendArgument(const std::string& str);
+  std::string GetArgumentsString();
+  void FinalizeTemplate();
+  const char* ParseS(const char* name);
+  const char* ParseT(const char* name);
+  const char* AppendOperatorString(const char* name);
+  void Save(const std::string& str, bool is_name);
+
+ private:
+  void Clear() {
+    parse_funcs_.clear();
+    function_name_.clear();
+    function_suffix_.clear();
+    first_save_.clear();
+    cur_state_.Clear();
+    saves_.clear();
+    template_saves_.clear();
+    while (!state_stack_.empty()) {
+      state_stack_.pop();
+    }
+    last_save_name_ = false;
+    template_found_ = false;
+  }
+
+  using parse_func_type = const char* (Demangler::*)(const char*);
+  parse_func_type parse_func_;
+  std::vector<parse_func_type> parse_funcs_;
+  std::vector<std::string> saves_;
+  std::vector<std::string> template_saves_;
+  bool last_save_name_;
+  bool template_found_;
+
+  std::string function_name_;
+  std::string function_suffix_;
+
+  struct StateData {
+    void Clear() {
+      str.clear();
+      args.clear();
+      prefix.clear();
+      suffixes.clear();
+      last_save.clear();
+    }
+
+    std::string str;
+    std::vector<std::string> args;
+    std::string prefix;
+    std::vector<std::string> suffixes;
+    std::string last_save;
+  };
+  std::stack<StateData> state_stack_;
+  std::string first_save_;
+  StateData cur_state_;
+
+  static const char* GetStringFromLength(const char* name, std::string* str);
+
+  // Parsing functions.
+  const char* ParseComplexString(const char* name);
+  const char* ParseComplexArgument(const char* name);
+  const char* ParseArgumentsAtTopLevel(const char* name);
+  const char* ParseArguments(const char* name);
+  const char* ParseTemplateArguments(const char* name);
+  const char* ParseTemplateArgumentsComplex(const char* name);
+  const char* ParseTemplateLiteral(const char* name);
+  const char* ParseFunctionArgument(const char* name);
+  const char* ParseFunctionName(const char* name);
+  const char* ParseFunctionNameTemplate(const char* name);
+  const char* ParseFunctionTemplateArguments(const char* name);
+  const char* FindFunctionName(const char* name);
+  const char* Fail(const char*) { return nullptr; }
+
+  // The default maximum string length string to process.
+  static constexpr size_t kMaxDefaultLength = 2048;
+
+  static constexpr const char* kTypes[] = {
+    "signed char",        // a
+    "bool",               // b
+    "char",               // c
+    "double",             // d
+    "long double",        // e
+    "float",              // f
+    "__float128",         // g
+    "unsigned char",      // h
+    "int",                // i
+    "unsigned int",       // j
+    nullptr,              // k
+    "long",               // l
+    "unsigned long",      // m
+    "__int128",           // n
+    "unsigned __int128",  // o
+    nullptr,              // p
+    nullptr,              // q
+    nullptr,              // r
+    "short",              // s
+    "unsigned short",     // t
+    nullptr,              // u
+    "void",               // v
+    "wchar_t",            // w
+    "long long",          // x
+    "unsigned long long", // y
+    "...",                // z
+  };
+
+  static constexpr const char* kDTypes[] = {
+    "auto",               // a
+    nullptr,              // b
+    nullptr,              // c
+    "decimal64",          // d
+    "decimal128",         // e
+    "decimal32",          // f
+    nullptr,              // g
+    "half",               // h
+    "char32_t",           // i
+    nullptr,              // j
+    nullptr,              // k
+    nullptr,              // l
+    nullptr,              // m
+    "decltype(nullptr)",  // n
+    nullptr,              // o
+    nullptr,              // p
+    nullptr,              // q
+    nullptr,              // r
+    "char16_t",           // s
+    nullptr,              // t
+    nullptr,              // u
+    nullptr,              // v
+    nullptr,              // w
+    nullptr,              // x
+    nullptr,              // y
+    nullptr,              // z
+  };
+
+  static constexpr const char* kSTypes[] = {
+    "std::allocator",     // a
+    "std::basic_string",  // b
+    nullptr,              // c
+    "std::iostream",      // d
+    nullptr,              // e
+    nullptr,              // f
+    nullptr,              // g
+    nullptr,              // h
+    "std::istream",       // i
+    nullptr,              // j
+    nullptr,              // k
+    nullptr,              // l
+    nullptr,              // m
+    nullptr,              // n
+    "std::ostream",       // o
+    nullptr,              // p
+    nullptr,              // q
+    nullptr,              // r
+    "std::string",        // s
+    nullptr,              // t
+    nullptr,              // u
+    nullptr,              // v
+    nullptr,              // w
+    nullptr,              // x
+    nullptr,              // y
+    nullptr,              // z
+  };
+};
+
+#endif  // __LIB_DEMANGLE_DEMANGLER_H
diff --git a/demangle/OWNERS b/demangle/OWNERS
new file mode 100644
index 0000000..6f7e4a3
--- /dev/null
+++ b/demangle/OWNERS
@@ -0,0 +1 @@
+cferris@google.com
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
new file mode 100644
index 0000000..66e5e58
--- /dev/null
+++ b/demangle/demangle.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cctype>
+#include <string>
+
+#include <demangle.h>
+
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
+static void Usage(const char* prog_name) {
+  printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
+  printf("\n");
+  printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
+  printf("reading from stdin otherwise.\n");
+  printf("\n");
+  printf("-c\tCompare against __cxa_demangle\n");
+  printf("\n");
+}
+
+static std::string DemangleWithCxa(const char* name) {
+  const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
+  if (cxa_demangle == nullptr) {
+    return name;
+  }
+
+  // The format of our demangler is slightly different from the cxa demangler
+  // so modify the cxa demangler output. Specifically, for templates, remove
+  // the spaces between '>' and '>'.
+  std::string demangled_str;
+  for (size_t i = 0; i < strlen(cxa_demangle); i++) {
+    if (i > 2 && cxa_demangle[i] == '>' && std::isspace(cxa_demangle[i - 1]) &&
+        cxa_demangle[i - 2] == '>') {
+      demangled_str.resize(demangled_str.size() - 1);
+    }
+    demangled_str += cxa_demangle[i];
+  }
+  return demangled_str;
+}
+
+static void Compare(const char* name, const std::string& demangled_name) {
+  std::string cxa_demangled_name(DemangleWithCxa(name));
+  if (cxa_demangled_name != demangled_name) {
+    printf("\nMismatch!\n");
+    printf("\tmangled name: %s\n", name);
+    printf("\tour demangle: %s\n", demangled_name.c_str());
+    printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
+    exit(1);
+  }
+}
+
+static int Filter(bool compare) {
+  char* line = nullptr;
+  size_t line_length = 0;
+
+  while ((getline(&line, &line_length, stdin)) != -1) {
+    char* p = line;
+    char* name;
+    while ((name = strstr(p, "_Z")) != nullptr) {
+      // Output anything before the identifier.
+      *name = 0;
+      printf("%s", p);
+      *name = '_';
+
+      // Extract the identifier.
+      p = name;
+      while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
+
+      // Demangle and output.
+      std::string identifier(name, p);
+      std::string demangled_name = demangle(identifier.c_str());
+      printf("%s", demangled_name.c_str());
+
+      if (compare) Compare(identifier.c_str(), demangled_name);
+    }
+    // Output anything after the last identifier.
+    printf("%s", p);
+  }
+
+  free(line);
+  return 0;
+}
+
+int main(int argc, char** argv) {
+#ifdef __BIONIC__
+  const char* prog_name = getprogname();
+#else
+  const char* prog_name = argv[0];
+#endif
+
+  bool compare = false;
+  int opt_char;
+  while ((opt_char = getopt(argc, argv, "c")) != -1) {
+    if (opt_char == 'c') {
+      compare = true;
+    } else {
+      Usage(prog_name);
+      return 1;
+    }
+  }
+
+  // With no arguments, act as a filter.
+  if (optind == argc) return Filter(compare);
+
+  // Otherwise demangle each argument.
+  while (optind < argc) {
+    const char* name = argv[optind++];
+    std::string demangled_name = demangle(name);
+    printf("%s\n", demangled_name.c_str());
+
+    if (compare) Compare(name, demangled_name);
+  }
+  return 0;
+}
diff --git a/demangle/demangle_fuzzer.cpp b/demangle/demangle_fuzzer.cpp
new file mode 100644
index 0000000..83fafc2
--- /dev/null
+++ b/demangle/demangle_fuzzer.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+
+#include "Demangler.h"
+
+extern "C" void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::vector<char> data_str(size + 1);
+  memcpy(data_str.data(), data, size);
+  data_str[size] = '\0';
+
+  Demangler demangler;
+  std::string demangled_name = demangler.Parse(data_str.data());
+  if (size != 0 && data_str[0] != '\0' && demangled_name.empty()) {
+    abort();
+  }
+}
diff --git a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h b/demangle/include/demangle.h
similarity index 61%
copy from fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
copy to demangle/include/demangle.h
index ab7ba73..01f1b80 100644
--- a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
+++ b/demangle/include/demangle.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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,16 +14,14 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef __LIB_DEMANGLE_H_
+#define __LIB_DEMANGLE_H_
 
-#include <map>
 #include <string>
 
-namespace android {
-namespace fs_mgr {
+// If the name cannot be demangled, the original name will be returned as
+// a std::string. If the name can be demangled, then the demangled name
+// will be returned as a std::string.
+std::string demangle(const char* name);
 
-bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
-                            const std::map<std::string, std::string>& images_path);
-
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
+#endif  // __LIB_DEMANGLE_H_
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 02a887e..716fe95 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -122,7 +122,6 @@
 
     shared_libs: [
         "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
         "android.hardware.fastboot@1.0",
         "android.hardware.health@2.0",
         "libadbd",
@@ -135,6 +134,8 @@
         "libfs_mgr",
         "libgsi",
         "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
         "liblog",
         "liblp",
         "libsparse",
@@ -144,10 +145,6 @@
     static_libs: [
         "libhealthhalutils",
     ],
-
-    header_libs: [
-        "libsnapshot_headers",
-    ]
 }
 
 cc_defaults {
@@ -190,11 +187,7 @@
     // will violate ODR.
     shared_libs: [],
 
-    header_libs: [
-        "avb_headers",
-        "bootimg_headers",
-    ],
-
+    header_libs: ["bootimg_headers"],
     static_libs: [
         "libziparchive",
         "libsparse",
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 7fba67c..8a72627 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -34,7 +34,6 @@
 #define FB_CMD_UPDATE_SUPER "update-super"
 #define FB_CMD_OEM "oem"
 #define FB_CMD_GSI "gsi"
-#define FB_CMD_SNAPSHOT_UPDATE "snapshot-update"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
@@ -67,4 +66,3 @@
 #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"
-#define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index dfd5690..1b09f79 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -25,7 +25,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <android/hardware/boot/1.1/IBootControl.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr.h>
@@ -40,15 +39,14 @@
 #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::boot::V1_1::MergeStatus;
 using ::android::hardware::fastboot::V1_0::Result;
 using ::android::hardware::fastboot::V1_0::Status;
-using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
+
+using namespace android::fs_mgr;
 
 struct VariableHandlers {
     // Callback to retrieve the value of a single variable.
@@ -104,8 +102,7 @@
             {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}},
-            {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}}};
+            {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}}};
 
     if (args.size() < 2) {
         return device->WriteFail("Missing argument");
@@ -198,6 +195,23 @@
     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");
@@ -326,7 +340,7 @@
 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);
+    slot_number_ = SlotNumberForSlotSuffix(slot_suffix);
     auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
     if (!super_device) {
         return;
@@ -336,7 +350,7 @@
 }
 
 bool PartitionBuilder::Write() {
-    auto metadata = builder_->Export();
+    std::unique_ptr<LpMetadata> metadata = builder_->Export();
     if (!metadata) {
         return false;
     }
@@ -367,7 +381,7 @@
         return device->WriteFail("Partition already exists");
     }
 
-    auto partition = builder->AddPartition(partition_name, 0);
+    Partition* partition = builder->AddPartition(partition_name, 0);
     if (!partition) {
         return device->WriteFail("Failed to add partition");
     }
@@ -423,15 +437,10 @@
         return device->WriteFail("Could not open super partition");
     }
 
-    auto partition = builder->FindPartition(partition_name);
+    Partition* partition = builder->FindPartition(partition_name);
     if (!partition) {
         return device->WriteFail("Partition does not exist");
     }
-
-    // Remove the updated flag to cancel any snapshots.
-    uint32_t attrs = partition->attributes();
-    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
-
     if (!builder->ResizePartition(partition, partition_size)) {
         return device->WriteFail("Not enough space to resize partition");
     }
@@ -441,42 +450,6 @@
     return device->WriteOkay("Partition resized");
 }
 
-void CancelPartitionSnapshot(FastbootDevice* device, const std::string& partition_name) {
-    PartitionBuilder builder(device, partition_name);
-    if (!builder.Valid()) return;
-
-    auto partition = builder->FindPartition(partition_name);
-    if (!partition) return;
-
-    // Remove the updated flag to cancel any snapshots.
-    uint32_t attrs = partition->attributes();
-    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
-
-    builder.Write();
-}
-
-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");
-    }
-
-    const auto& partition_name = args[1];
-    if (LogicalPartitionExists(device, partition_name)) {
-        CancelPartitionSnapshot(device, partition_name);
-    }
-
-    int ret = Flash(device, partition_name);
-    if (ret < 0) {
-        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
-    }
-    return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
-}
-
 bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteFail("Invalid arguments");
@@ -493,7 +466,7 @@
 class AutoMountMetadata {
   public:
     AutoMountMetadata() {
-        android::fs_mgr::Fstab proc_mounts;
+        Fstab proc_mounts;
         if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
             LOG(ERROR) << "Could not read /proc/mounts";
             return;
@@ -521,7 +494,7 @@
     explicit operator bool() const { return mounted_; }
 
   private:
-    android::fs_mgr::Fstab fstab_;
+    Fstab fstab_;
     bool mounted_ = false;
     bool should_unmount_ = false;
 };
@@ -551,40 +524,3 @@
     }
     return device->WriteStatus(FastbootResult::OKAY, "Success");
 }
-
-bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {
-    // Note that we use the HAL rather than mounting /metadata, since we want
-    // our results to match the bootloader.
-    auto hal = device->boot_control_hal();
-    if (!hal) return device->WriteFail("Not supported");
-
-    android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
-    if (!hal11) return device->WriteFail("Not supported");
-
-    // If no arguments, return the same thing as a getvar. Note that we get the
-    // HAL first so we can return "not supported" before we return the less
-    // specific error message below.
-    if (args.size() < 2 || args[1].empty()) {
-        std::string message;
-        if (!GetSnapshotUpdateStatus(device, {}, &message)) {
-            return device->WriteFail("Could not determine update status");
-        }
-        device->WriteInfo(message);
-        return device->WriteOkay("");
-    }
-
-    if (args.size() != 2 || args[1] != "cancel") {
-        return device->WriteFail("Invalid arguments");
-    }
-
-    MergeStatus status = hal11->getSnapshotMergeStatus();
-    switch (status) {
-        case MergeStatus::SNAPSHOTTED:
-        case MergeStatus::MERGING:
-            hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED);
-            break;
-        default:
-            break;
-    }
-    return device->WriteStatus(FastbootResult::OKAY, "Success");
-}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index c1324bc..afd6d08 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -19,7 +19,7 @@
 #include <string>
 #include <vector>
 
-constexpr unsigned int kMaxDownloadSizeDefault = 0x10000000;
+constexpr unsigned int kMaxDownloadSizeDefault = 0x20000000;
 
 class FastbootDevice;
 
@@ -49,4 +49,3 @@
 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);
-bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index d3c2bda..56fafab 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -54,7 +54,6 @@
               {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
               {FB_CMD_OEM, OemCmdHandler},
               {FB_CMD_GSI, GsiHandler},
-              {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()),
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 102ebdb..99854c9 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -32,7 +32,6 @@
 #include <fstab/fstab.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
-#include <libsnapshot/snapshot.h>
 #include <sparse/sparse.h>
 
 #include "fastboot_device.h"
@@ -172,11 +171,6 @@
         if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
             continue;
         }
-        std::string group_name = GetPartitionGroupName(old_metadata->groups[partition.group_index]);
-        // Skip partitions in the COW group
-        if (group_name == android::snapshot::kCowGroupName) {
-            continue;
-        }
         partitions_to_keep.emplace(partition_name);
     }
 
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 5066046..1014291 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -258,7 +258,7 @@
         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;
+            return bytes_read_total;
         }
         bytes_read_total += bytes_read_now;
         char_data += bytes_read_now;
@@ -279,7 +279,7 @@
         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;
+            return bytes_written_total;
         }
         bytes_written_total += bytes_written_now;
         char_data += bytes_written_now;
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index b3f2d5f..2ebd57d 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -56,20 +56,12 @@
     if (!path) {
         return false;
     }
-
-    CreateLogicalPartitionParams params = {
-            .block_device = *path,
-            .metadata_slot = slot_number,
-            .partition_name = partition_name,
-            .force_writable = true,
-            .timeout_ms = 5s,
-    };
     std::string dm_path;
-    if (!CreateLogicalPartition(params, &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); };
+    auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name, 5s); };
     *handle = PartitionHandle(dm_path, std::move(closer));
     return true;
 }
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 6e613d6..130a3cf 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -23,7 +23,6 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android/hardware/boot/1.1/IBootControl.h>
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
 #include <healthhalutils/HealthHalUtils.h>
@@ -35,11 +34,9 @@
 
 using ::android::hardware::boot::V1_0::BoolResult;
 using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::boot::V1_1::MergeStatus;
 using ::android::hardware::fastboot::V1_0::FileSystemType;
 using ::android::hardware::fastboot::V1_0::Result;
 using ::android::hardware::fastboot::V1_0::Status;
-using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
 using namespace android::fs_mgr;
 
 constexpr char kFastbootProtocolVersion[] = "0.4";
@@ -427,34 +424,3 @@
     *message = fs_mgr_get_super_partition_name(slot_number);
     return true;
 }
-
-bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& /* args */,
-                             std::string* message) {
-    // Note that we use the HAL rather than mounting /metadata, since we want
-    // our results to match the bootloader.
-    auto hal = device->boot_control_hal();
-    if (!hal) {
-        *message = "not supported";
-        return false;
-    }
-
-    android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
-    if (!hal11) {
-        *message = "not supported";
-        return false;
-    }
-
-    MergeStatus status = hal11->getSnapshotMergeStatus();
-    switch (status) {
-        case MergeStatus::SNAPSHOTTED:
-            *message = "snapshotted";
-            break;
-        case MergeStatus::MERGING:
-            *message = "merging";
-            break;
-        default:
-            *message = "none";
-            break;
-    }
-    return true;
-}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 4dec10f..015a4c5 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -61,8 +61,6 @@
                      std::string* message);
 bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
                            std::string* message);
-bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
-                             std::string* message);
 
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7ce7c7c..c8caa67 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -51,7 +51,6 @@
 #include <utility>
 #include <vector>
 
-#include <android-base/endian.h>
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
@@ -60,7 +59,6 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <build/version.h>
-#include <libavb/libavb.h>
 #include <liblp/liblp.h>
 #include <platform_tools_version.h>
 #include <sparse/sparse.h>
@@ -146,13 +144,14 @@
     { "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 },
+    { "product_services",
+                  "product_services.img",
+                                      "product_services.sig",
+                                                      "product_services",
+                                                                  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 },
@@ -162,10 +161,6 @@
                                                       "vbmeta_system",
                                                                   true,  ImageType::BootCritical },
     { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
-    { "vendor_boot",
-                  "vendor_boot.img",  "vendor_boot.sig",
-                                                      "vendor_boot",
-                                                                  true,  ImageType::BootCritical },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
 };
@@ -394,11 +389,6 @@
             " 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"
-            " snapshot-update cancel     On devices that support snapshot-based updates, cancel\n"
-            "                            an in-progress update. This may make the device\n"
-            "                            unbootable until it is reflashed.\n"
             "\n"
             "boot image:\n"
             " boot KERNEL [RAMDISK [SECOND]]\n"
@@ -512,8 +502,9 @@
 
 static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
                           std::vector<char>* out) {
+    ZipString zip_entry_name(entry_name.c_str());
     ZipEntry zip_entry;
-    if (FindEntry(zip, entry_name, &zip_entry) != 0) {
+    if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
         return false;
     }
@@ -623,8 +614,9 @@
 static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
     unique_fd fd(make_temporary_fd(entry_name));
 
+    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
-    if (FindEntry(zip, entry_name, &zip_entry) != 0) {
+    if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
         errno = ENOENT;
         return -1;
@@ -879,7 +871,7 @@
         return false;
     }
 
-    if (sparse_file* s = sparse_file_import(fd, false, false)) {
+    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 {
@@ -924,50 +916,33 @@
     return load_buf_fd(fd.release(), buf);
 }
 
-static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_boot) {
+static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
     // Buffer needs to be at least the size of the VBMeta struct which
     // is 256 bytes.
     if (buf->sz < 256) {
         return;
     }
 
+    int fd = make_temporary_fd("vbmeta rewriting");
+
     std::string data;
     if (!android::base::ReadFdToString(buf->fd, &data)) {
         die("Failed reading from vbmeta");
     }
 
-    uint64_t vbmeta_offset = 0;
-    if (vbmeta_in_boot) {
-        // Tries to locate top-level vbmeta from boot.img footer.
-        uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
-        if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
-            die("Failed to find AVB_FOOTER at offset: %" PRId64, footer_offset);
-        }
-        const AvbFooter* footer = reinterpret_cast<const AvbFooter*>(data.c_str() + footer_offset);
-        vbmeta_offset = be64toh(footer->vbmeta_offset);
-    }
-    // Ensures there is AVB_MAGIC at vbmeta_offset.
-    if (0 != data.compare(vbmeta_offset, AVB_MAGIC_LEN, AVB_MAGIC)) {
-        die("Failed to find AVB_MAGIC at offset: %" PRId64, vbmeta_offset);
-    }
-
-    fprintf(stderr, "Rewriting vbmeta struct at offset: %" PRId64 "\n", vbmeta_offset);
-
     // There's a 32-bit big endian |flags| field at offset 120 where
     // bit 0 corresponds to disable-verity and bit 1 corresponds to
     // disable-verification.
     //
     // See external/avb/libavb/avb_vbmeta_image.h for the layout of
     // the VBMeta struct.
-    uint64_t flags_offset = 123 + vbmeta_offset;
     if (g_disable_verity) {
-        data[flags_offset] |= 0x01;
+        data[123] |= 0x01;
     }
     if (g_disable_verification) {
-        data[flags_offset] |= 0x02;
+        data[123] |= 0x02;
     }
 
-    int fd = make_temporary_fd("vbmeta rewriting");
     if (!android::base::WriteStringToFd(data, fd)) {
         die("Failed writing to modified vbmeta");
     }
@@ -976,25 +951,14 @@
     lseek(fd, 0, SEEK_SET);
 }
 
-static bool has_vbmeta_partition() {
-    std::string partition_type;
-    return fb->GetVar("partition-type:vbmeta", &partition_type) == fastboot::SUCCESS ||
-           fb->GetVar("partition-type:vbmeta_a", &partition_type) == fastboot::SUCCESS ||
-           fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
-}
-
 static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
 {
     sparse_file** s;
 
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
-    if (g_disable_verity || g_disable_verification) {
-        if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
-            rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);
-        } else if (!has_vbmeta_partition() &&
-                   (partition == "boot" || partition == "boot_a" || partition == "boot_b")) {
-            rewrite_vbmeta_buffer(buf, true /* vbmeta_in_boot */ );
-        }
+    if ((g_disable_verity || g_disable_verification) &&
+        (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b")) {
+        rewrite_vbmeta_buffer(buf);
     }
 
     switch (buf->type) {
@@ -1219,14 +1183,6 @@
     target_sparse_limit = -1;
 }
 
-static void CancelSnapshotIfNeeded() {
-    std::string merge_status = "none";
-    if (fb->GetVar(FB_VAR_SNAPSHOT_UPDATE_STATUS, &merge_status) == fastboot::SUCCESS &&
-        merge_status != "none") {
-        fb->SnapshotUpdateCommand("Cancel");
-    }
-}
-
 class ImageSource {
   public:
     virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
@@ -1279,8 +1235,6 @@
     DetermineSecondarySlot();
     CollectImages();
 
-    CancelSnapshotIfNeeded();
-
     // First flash boot partitions. We allow this to happen either in userspace
     // or in bootloader fastboot.
     FlashImages(boot_images_);
@@ -1630,76 +1584,6 @@
     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;
@@ -2076,32 +1960,12 @@
             } 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 if (command == "snapshot-update") {
-            std::string arg;
-            if (!args.empty()) {
-                arg = next_arg(&args);
-            }
-            if (!arg.empty() && arg != "cancel") {
-                syntax_error("expected: snapshot-update [cancel]");
-            }
-            fb->SnapshotUpdateCommand(arg);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
     }
 
     if (wants_wipe) {
-        if (force_flash) {
-            CancelSnapshotIfNeeded();
-        }
         std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
         for (const auto& partition : partitions) {
             std::string partition_type;
@@ -2113,10 +1977,10 @@
             if (partition == "userdata" && set_fbe_marker) {
                 fprintf(stderr, "setting FBE marker on initial userdata...\n");
                 std::string initial_userdata_dir = create_fbemarker_tmpdir();
-                fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir);
+                fb_perform_format(partition, 1, "", "", initial_userdata_dir);
                 delete_fbemarker_tmpdir(initial_userdata_dir);
             } else {
-                fb_perform_format(partition, 1, partition_type, "", "");
+                fb_perform_format(partition, 1, "", "", "");
             }
         }
     }
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 6a5ad20..fea0a77 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -122,12 +122,6 @@
                       response, info);
 }
 
-RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
-                                              std::vector<std::string>* info) {
-    std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
-    return RawCommand(raw, response, info);
-}
-
 RetCode FastBootDriver::FlashPartition(const std::string& partition,
                                        const std::vector<char>& data) {
     RetCode ret;
@@ -297,7 +291,7 @@
 RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,
                                     std::vector<std::string>* info) {
     RetCode ret;
-    int dsize = 0;
+    int dsize;
     if ((ret = RawCommand(FB_CMD_UPLOAD, response, info, &dsize))) {
         error_ = "Upload request failed: " + error_;
         return ret;
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 7265632..af02637 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -104,8 +104,6 @@
                       std::vector<std::string>* info = nullptr);
     RetCode Upload(const std::string& outfile, std::string* response = nullptr,
                    std::vector<std::string>* info = nullptr);
-    RetCode SnapshotUpdateCommand(const std::string& command, 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);
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 8a3c213..f597f50 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -106,7 +106,7 @@
     kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
 
     if (kr != 0) {
-        WARN("Couldn't create a device interface iterator: (%08x)\n", kr);
+        ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
         return -1;
     }
 
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index bf840f8..b00edb3 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -195,28 +195,25 @@
 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 %zu\n", len);
     if (nullptr != handle_) {
-        while (len > 0) {
-            size_t xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
+        while (1) {
+            int 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: %lu, expected: %zu, errno: %d\n", read, xfer, errno);
-            if (ret == 0) {
+            DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
+            if (ret) {
+                return read;
+            } else {
                 // assume ERROR_INVALID_HANDLE indicates we are disconnected
                 if (errno == ERROR_INVALID_HANDLE)
                     usb_kick(handle_.get());
                 break;
             }
-            count += read;
-            len -= read;
-            data = (char*)data + read;
-
-            if (xfer != read || len == 0) return count;
+            // else we timed out - try again
         }
     } else {
         DBG("usb_read NULL handle\n");
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 900d6ea..d02b37f 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -53,10 +53,6 @@
     exit(EXIT_FAILURE);
 }
 
-void die(const std::string& str) {
-    die("%s", str.c_str());
-}
-
 void set_verbose() {
     g_verbose = true;
 }
diff --git a/fastboot/util.h b/fastboot/util.h
index c719df2..2535414 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -15,7 +15,4 @@
 // 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 eb737bb..4ee9624 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -35,7 +35,6 @@
     export_include_dirs: ["include"],
     include_dirs: ["system/vold"],
     srcs: [
-        "file_wait.cpp",
         "fs_mgr.cpp",
         "fs_mgr_format.cpp",
         "fs_mgr_verity.cpp",
@@ -73,8 +72,6 @@
     whole_static_libs: [
         "liblogwrap",
         "libdm",
-        "libext2_uuid",
-        "libfscrypt",
         "libfstab",
     ],
     cppflags: [
@@ -124,14 +121,9 @@
     shared_libs: [
         "libbootloader_message",
         "libbase",
-        "libcutils",
         "libcrypto",
-        "libext4_utils",
         "libfec",
         "libfs_mgr",
-        "liblog",
-        "liblp",
-        "libselinux",
     ],
     header_libs: [
         "libcutils_headers",
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index f579078..f89e598 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -1,27 +1,33 @@
-Android OverlayFS Integration with adb Remount
+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 efficient for a development cycle.
+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.
 
-Limited memory systems use read-only types of file systems or logical resizable
-Android partitions (LRAPs). These file systems land system partition images
-right-sized, and have been deduped at the block level to compress the content.
-This means that a remount either isn’t possible, or isn't useful because of
-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.
 
-OverlayFS resolves these debug scenarios with the _adb disable-verity_ and
-_adb remount_ commands, which set up backing storage for a writable file
-system as an upper reference, and mount the lower reference on top.
+Operations
+----------
 
-Performing a remount
---------------------
+### Cookbook
 
-Use the following sequence to perform the remount.
+The typical action to utilize the remount facility is:
 
     $ adb root
     $ adb disable-verity
@@ -30,11 +36,11 @@
     $ adb root
     $ adb remount
 
-Then enter one of the following sequences:
+Followed by one of the following:
 
-    $ adb shell stop
+    $ adb stop
     $ adb sync
-    $ adb shell start
+    $ adb start
     $ adb reboot
 
 *or*
@@ -42,87 +48,72 @@
     $ adb push <source> <destination>
     $ adb reboot
 
-Note that you can replace these two lines:
+Note that the sequence above:
 
     $ adb disable-verity
     $ adb reboot
 
-with this line:
+*or*
+
+    $ adb remount
+
+can be replaced in both places with:
 
     $ adb remount -R
 
-**Note:** _adb reboot -R_ won’t reboot if the device is already in the adb remount state.
+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 file-system remount,
-or one wrapped by OverlayFS is automatically determined based on
-a probe of the file-system types and space remaining.
+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 uses either the
+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 *LRAP*.
-It is also possible for an A/B device to use the system_<other> partition
-for backing storage. eg: if booting off system_a+vendor_a, use system_b.
+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 when the
-*mount_all* commands are run in init RC scripts.
+process and can occur at first stage init, or at the
+mount_all init rc commands.
 
-By attaching OverlayFS early, SEpolicy or init can be pushed and used after the exec phases of each stage.
+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
 -------
 
-- Backing storage requires more space than immutable storage, as backing is
-  done file by file. Be mindful of wasted space. For example, defining
-  **BOARD_IMAGE_PARTITION_RESERVED_SIZE** has a negative impact on the
-  right-sizing of images and requires more free dynamic partition space.
-- The kernel requires **CONFIG_OVERLAY_FS=y**. If the kernel version is higher
-  than 4.4, it requires source to be in line with android-common kernels. 
-  The patch series is available on the upstream mailing list and the latest as
-  of Sep 5 2019 is https://www.spinics.net/lists/linux-mtd/msg08331.html
-  This patch adds an override_creds _mount_ option to OverlayFS that
+- 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.
-  For 4.19 and higher a rework of the xattr handling to deal with recursion
-  is required. https://patchwork.kernel.org/patch/11117145/ is a start of that
-  adjustment.
-- _adb enable-verity_ frees up OverlayFS and reverts the device to the state
-  prior to content updates. The update engine performs a full OTA.
-- _adb remount_ overrides are incompatible with OTA resources, so the update
-  engine may not run if fs_mgr_overlayfs_is_setup() returns true.
-- If a dynamic partition runs out of space, making a logical partition larger
-  may fail because of the scratch partition. If this happens, clear the scratch
-  storage by running either either _fastboot flashall_ or _adb enable-verity_.
-  Then reinstate the overrides and continue.
+- *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.
-- 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.
+- 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.
diff --git a/fs_mgr/file_wait.cpp b/fs_mgr/file_wait.cpp
deleted file mode 100644
index cbf6845..0000000
--- a/fs_mgr/file_wait.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-// 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 75ebd94..f753f54 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -56,9 +56,7 @@
 #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 <fscrypt/fscrypt.h>
 #include <libdm/dm.h>
 #include <liblp/metadata_format.h>
 #include <linux/fs.h>
@@ -85,9 +83,6 @@
 
 #define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
 
-// FIXME: this should be in system/extras
-#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
-
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
 using android::base::Basename;
@@ -121,6 +116,28 @@
     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,
+                          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;
+    }
+}
+
 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 =
@@ -226,11 +243,13 @@
         } else {
             LINFO << "Running " << E2FSCK_BIN << " on " << realpath(blk_device);
             if (should_force_check(*fs_stat)) {
-                ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_forced_argv), e2fsck_forced_argv,
-                                          &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+                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), nullptr, 0);
             } else {
-                ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, &status, false,
-                                          LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+                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), nullptr, 0);
             }
 
             if (ret < 0) {
@@ -248,12 +267,14 @@
 
         if (should_force_check(*fs_stat)) {
             LINFO << "Running " << F2FS_FSCK_BIN << " -f " << realpath(blk_device);
-            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
-                                      &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+            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 = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
-                                      LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
+            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 */
@@ -331,7 +352,8 @@
 static bool run_tune2fs(const char* argv[], int argc) {
     int ret;
 
-    ret = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_KLOG, true, nullptr);
+    ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true,
+                                  LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0);
     return ret == 0;
 }
 
@@ -416,43 +438,25 @@
 // Enable file-based encryption if needed.
 static void tune_encrypt(const std::string& blk_device, const FstabEntry& entry,
                          const struct ext4_super_block* sb, int* fs_stat) {
-    if (!entry.fs_mgr_flags.file_encryption) {
-        return;  // Nothing needs done.
-    }
-    std::vector<std::string> features_needed;
-    if ((sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) == 0) {
-        features_needed.emplace_back("encrypt");
-    }
-    android::fscrypt::EncryptionOptions options;
-    if (!android::fscrypt::ParseOptions(entry.encryption_options, &options)) {
-        LERROR << "Unable to parse encryption options on " << blk_device << ": "
-               << entry.encryption_options;
+    bool has_encrypt = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) != 0;
+    bool want_encrypt = entry.fs_mgr_flags.file_encryption;
+
+    if (has_encrypt || !want_encrypt) {
         return;
     }
-    if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) != 0) {
-        // We can only use this policy on ext4 if the "stable_inodes" feature
-        // is set on the filesystem, otherwise shrinking will break encrypted files.
-        if ((sb->s_feature_compat & cpu_to_le32(EXT4_FEATURE_COMPAT_STABLE_INODES)) == 0) {
-            features_needed.emplace_back("stable_inodes");
-        }
-    }
-    if (features_needed.size() == 0) {
-        return;
-    }
+
     if (!tune2fs_available()) {
         LERROR << "Unable to enable ext4 encryption on " << blk_device
                << " because " TUNE2FS_BIN " is missing";
         return;
     }
 
-    auto flags = android::base::Join(features_needed, ',');
-    auto flag_arg = "-O"s + flags;
-    const char* argv[] = {TUNE2FS_BIN, flag_arg.c_str(), blk_device.c_str()};
+    const char* argv[] = {TUNE2FS_BIN, "-Oencrypt", blk_device.c_str()};
 
-    LINFO << "Enabling ext4 flags " << flags << " on " << blk_device;
+    LINFO << "Enabling ext4 encryption on " << blk_device;
     if (!run_tune2fs(argv, ARRAY_SIZE(argv))) {
         LERROR << "Failed to run " TUNE2FS_BIN " to enable "
-               << "ext4 flags " << flags << " on " << blk_device;
+               << "ext4 encryption on " << blk_device;
         *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;
     }
 }
@@ -869,22 +873,37 @@
     }
 }
 
-static bool call_vdc(const std::vector<std::string>& args, int* ret) {
+static bool call_vdc(const std::vector<std::string>& args) {
     std::vector<char const*> argv;
     argv.emplace_back("/system/bin/vdc");
     for (auto& arg : args) {
         argv.emplace_back(arg.c_str());
     }
     LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
-    int err = logwrap_fork_execvp(argv.size(), argv.data(), ret, false, LOG_ALOG, false, nullptr);
+    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;
+    }
+    LOG(DEBUG) << "vdc finished successfully";
+    return true;
+}
+
+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";
-    if (ret != nullptr) {
-        *ret = WEXITSTATUS(*ret);
-    }
+    *ret = WEXITSTATUS(*ret);
     return true;
 }
 
@@ -910,27 +929,22 @@
   public:
     CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
 
-    bool NeedsCheckpoint() {
-        if (needs_checkpoint_ != UNKNOWN) {
-            return needs_checkpoint_ == YES;
-        }
-        if (!call_vdc({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
-            LERROR << "Failed to find if checkpointing is needed. Assuming no.";
-            needs_checkpoint_ = NO;
-        }
-        return needs_checkpoint_ == YES;
-    }
-
     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}, nullptr);
+            call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device});
         }
 
-        if (!NeedsCheckpoint()) {
+        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;
         }
 
@@ -1137,7 +1151,8 @@
             continue;
         }
 
-        if (current_entry.fs_mgr_flags.wait && !WaitForFile(current_entry.blk_device, 20s)) {
+        if (current_entry.fs_mgr_flags.wait &&
+            !fs_mgr_wait_for_file(current_entry.blk_device, 20s)) {
             LERROR << "Skipping '" << current_entry.blk_device << "' during mount_all";
             continue;
         }
@@ -1200,8 +1215,7 @@
                 encryptable = status;
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
                     if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
-                                   attempted_entry.mount_point},
-                                  nullptr)) {
+                                   attempted_entry.mount_point})) {
                         LERROR << "Encryption failed";
                         return FS_MGR_MNTALL_FAIL;
                     }
@@ -1273,8 +1287,7 @@
         } 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},
-                          nullptr)) {
+                           attempted_entry.mount_point})) {
                 ++error_count;
             }
             encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@@ -1340,7 +1353,7 @@
                 continue;
             }
         } else if ((current_entry.fs_mgr_flags.verify)) {
-            if (!fs_mgr_teardown_verity(&current_entry)) {
+            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;
@@ -1351,68 +1364,6 @@
     return ret;
 }
 
-static std::string GetUserdataBlockDevice() {
-    Fstab fstab;
-    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
-        LERROR << "Failed to read /proc/mounts";
-        return "";
-    }
-    auto entry = GetEntryForMountPoint(&fstab, "/data");
-    if (entry == nullptr) {
-        LERROR << "Didn't find /data mount point in /proc/mounts";
-        return "";
-    }
-    return entry->blk_device;
-}
-
-int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
-    const std::string& block_device = GetUserdataBlockDevice();
-    LINFO << "Userdata is mounted on " << block_device;
-    auto entry = std::find_if(fstab->begin(), fstab->end(), [&block_device](const FstabEntry& e) {
-        if (e.mount_point != "/data") {
-            return false;
-        }
-        if (e.blk_device == block_device) {
-            return true;
-        }
-        DeviceMapper& dm = DeviceMapper::Instance();
-        std::string path;
-        if (!dm.GetDmDevicePathByName("userdata", &path)) {
-            return false;
-        }
-        return path == block_device;
-    });
-    if (entry == fstab->end()) {
-        LERROR << "Can't find /data in fstab";
-        return -1;
-    }
-    if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
-        LINFO << "Userdata doesn't support checkpointing. Nothing to do";
-        return 0;
-    }
-    CheckpointManager checkpoint_manager;
-    if (!checkpoint_manager.NeedsCheckpoint()) {
-        LINFO << "Checkpointing not needed. Don't remount";
-        return 0;
-    }
-    if (entry->fs_mgr_flags.checkpoint_fs) {
-        // Userdata is f2fs, simply remount it.
-        if (!checkpoint_manager.Update(&(*entry))) {
-            LERROR << "Failed to remount userdata in checkpointing mode";
-            return -1;
-        }
-        if (mount(entry->blk_device.c_str(), entry->mount_point.c_str(), "none",
-                  MS_REMOUNT | entry->flags, entry->fs_options.c_str()) != 0) {
-            LERROR << "Failed to remount userdata in checkpointing mode";
-            return -1;
-        }
-    } else {
-        // STOPSHIP(b/143970043): support remounting for ext4.
-        LWARNING << "Remounting into checkpointing is not supported for ex4. Proceed with caution";
-    }
-    return 0;
-}
-
 // 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) {
@@ -1472,7 +1423,7 @@
         }
 
         // First check the filesystem if requested.
-        if (fstab_entry.fs_mgr_flags.wait && !WaitForFile(n_blk_device, 20s)) {
+        if (fstab_entry.fs_mgr_flags.wait && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
             continue;
         }
@@ -1675,7 +1626,7 @@
             fprintf(zram_fp.get(), "%" PRId64 "\n", entry.zram_size);
         }
 
-        if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {
+        if (entry.fs_mgr_flags.wait && !fs_mgr_wait_for_file(entry.blk_device, 20s)) {
             LERROR << "Skipping mkswap for '" << entry.blk_device << "'";
             ret = false;
             continue;
@@ -1686,8 +1637,10 @@
                 MKSWAP_BIN,
                 entry.blk_device.c_str(),
         };
-        int err = logwrap_fork_execvp(ARRAY_SIZE(mkswap_argv), mkswap_argv, nullptr, false,
-                                      LOG_KLOG, false, nullptr);
+        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 " << entry.blk_device;
             ret = false;
@@ -1713,6 +1666,38 @@
     return ret;
 }
 
+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;
+
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        LERROR << "Failed to read default fstab";
+        return false;
+    }
+
+    for (const auto& entry : fstab) {
+        if (entry.fs_mgr_flags.avb) {
+            *mode = VERITY_MODE_RESTART;  // avb only supports restart mode.
+            break;
+        } else if (!entry.fs_mgr_flags.verify) {
+            continue;
+        }
+
+        int current;
+        if (load_verity_state(entry, &current) < 0) {
+            continue;
+        }
+        if (current != VERITY_MODE_DEFAULT) {
+            *mode = current;
+            break;
+        }
+    }
+
+    return true;
+}
+
 bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
     if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
         return false;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 0dcb9fe..ee6ffdb 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -38,7 +38,6 @@
 #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"
@@ -52,53 +51,45 @@
 using DmTargetZero = android::dm::DmTargetZero;
 using DmTargetLinear = android::dm::DmTargetLinear;
 
-static bool GetPhysicalPartitionDevicePath(const IPartitionOpener& opener,
-                                           const LpMetadata& metadata,
+static bool GetPhysicalPartitionDevicePath(const LpMetadata& metadata,
                                            const LpMetadataBlockDevice& block_device,
-                                           const std::string& super_device, std::string* result) {
+                                           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.)
-    std::string name = GetBlockDevicePartitionName(block_device);
-    std::string dev_string = opener.GetDeviceString(name);
     if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
-        dev_string = opener.GetDeviceString(super_device);
+        path = super_device;
     }
-
-    // Note: device-mapper will not accept symlinks, so we must use realpath
-    // here. If the device string is a major:minor sequence, we don't need to
-    // to call Realpath (it would not work anyway).
-    if (android::base::StartsWith(dev_string, "/")) {
-        if (!android::base::Realpath(dev_string, result)) {
-            PERROR << "realpath: " << dev_string;
-            return false;
-        }
-    } else {
-        *result = dev_string;
+    if (!android::base::Realpath(path, result)) {
+        PERROR << "realpath: " << path;
+        return false;
     }
     return true;
 }
 
-bool CreateDmTableInternal(const CreateLogicalPartitionParams& params, DmTable* table) {
-    const auto& super_device = params.block_device;
-
+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 < params.partition->num_extents; i++) {
-        const auto& extent = params.metadata->extents[params.partition->first_extent_index + i];
+    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 = params.metadata->block_devices[extent.target_source];
-                std::string dev_string;
-                if (!GetPhysicalPartitionDevicePath(*params.partition_opener, *params.metadata,
-                                                    block_device, super_device, &dev_string)) {
+                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, dev_string,
+                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
                                                           extent.target_data);
                 break;
             }
@@ -111,19 +102,40 @@
         }
         sector += extent.num_sectors;
     }
-    if (params.partition->attributes & LP_PARTITION_ATTR_READONLY) {
+    if (partition.attributes & LP_PARTITION_ATTR_READONLY) {
         table->set_readonly(true);
     }
-    if (params.force_writable) {
-        table->set_readonly(false);
-    }
     return true;
 }
 
-bool CreateDmTable(CreateLogicalPartitionParams params, DmTable* table) {
-    CreateLogicalPartitionParams::OwnedData owned_data;
-    if (!params.InitDefaults(&owned_data)) return false;
-    return CreateDmTableInternal(params, table);
+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 (!fs_mgr_wait_for_file(*path, timeout_ms, FileWaitMode::Exists)) {
+            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) {
@@ -142,20 +154,13 @@
 }
 
 bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
-    CreateLogicalPartitionParams params = {
-            .block_device = super_device,
-            .metadata = &metadata,
-    };
     for (const auto& partition : metadata.partitions) {
         if (!partition.num_extents) {
             LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
             continue;
         }
-
-        params.partition = &partition;
-
-        std::string ignore_path;
-        if (!CreateLogicalPartition(params, &ignore_path)) {
+        std::string path;
+        if (!CreateLogicalPartition(metadata, partition, false, {}, super_device, &path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
@@ -163,98 +168,49 @@
     return true;
 }
 
-bool CreateLogicalPartitionParams::InitDefaults(CreateLogicalPartitionParams::OwnedData* owned) {
-    if (block_device.empty()) {
-        LOG(ERROR) << "block_device is required for CreateLogicalPartition";
-        return false;
+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;
+}
 
-    if (!partition_opener) {
-        owned->partition_opener = std::make_unique<PartitionOpener>();
-        partition_opener = owned->partition_opener.get();
-    }
-
-    // Read metadata if needed.
+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) {
-        if (!metadata_slot) {
-            LOG(ERROR) << "Either metadata or a metadata slot must be specified.";
-            return false;
-        }
-        auto slot = *metadata_slot;
-        if (owned->metadata = ReadMetadata(*partition_opener, block_device, slot);
-            !owned->metadata) {
-            LOG(ERROR) << "Could not read partition table for: " << block_device;
-            return false;
-        }
-        metadata = owned->metadata.get();
+        LOG(ERROR) << "Could not read partition table.";
+        return true;
     }
-
-    // Find the partition by name if needed.
-    if (!partition) {
-        for (const auto& metadata_partition : metadata->partitions) {
-            if (android::fs_mgr::GetPartitionName(metadata_partition) == partition_name) {
-                partition = &metadata_partition;
-                break;
-            }
-        }
-    }
-    if (!partition) {
-        LERROR << "Could not find any partition with name: " << partition_name;
-        return false;
-    }
-    if (partition_name.empty()) {
-        partition_name = android::fs_mgr::GetPartitionName(*partition);
-    } else if (partition_name != android::fs_mgr::GetPartitionName(*partition)) {
-        LERROR << "Inconsistent partition_name " << partition_name << " with partition "
-               << android::fs_mgr::GetPartitionName(*partition);
-        return false;
-    }
-
-    if (device_name.empty()) {
-        device_name = partition_name;
-    }
-
-    return true;
+    return CreateLogicalPartition(block_device, *metadata.get(), partition_name, force_writable,
+                                  timeout_ms, path);
 }
 
-bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) {
-    CreateLogicalPartitionParams::OwnedData owned_data;
-    if (!params.InitDefaults(&owned_data)) return false;
-
-    DmTable table;
-    if (!CreateDmTableInternal(params, &table)) {
-        return false;
-    }
-
+bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
     DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {
-        return false;
+    std::string path;
+    if (timeout_ms > std::chrono::milliseconds::zero()) {
+        dm.GetDmDevicePathByName(name, &path);
     }
-    LINFO << "Created logical partition " << params.device_name << " on device " << *path;
-    return true;
-}
-
-std::string CreateLogicalPartitionParams::GetDeviceName() const {
-    if (!device_name.empty()) return device_name;
-    return GetPartitionName();
-}
-
-std::string CreateLogicalPartitionParams::GetPartitionName() const {
-    if (!partition_name.empty()) return partition_name;
-    if (partition) return android::fs_mgr::GetPartitionName(*partition);
-    return "<unknown partition>";
-}
-
-bool UnmapDevice(const std::string& name) {
-    DeviceMapper& dm = DeviceMapper::Instance();
     if (!dm.DeleteDevice(name)) {
         return false;
     }
+    if (!path.empty() && !fs_mgr_wait_for_file(path, timeout_ms, FileWaitMode::DoesNotExist)) {
+        LERROR << "Timed out waiting for device path to unlink: " << path;
+        return false;
+    }
     return true;
 }
 
-bool DestroyLogicalPartition(const std::string& name) {
-    if (!UnmapDevice(name)) {
+bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
+    if (!UnmapDevice(name, timeout_ms)) {
         return false;
     }
     LINFO << "Unmapped logical partition " << name;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index acf4d7b..1c6652a 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -76,8 +76,8 @@
             "/system/bin/mke2fs", "-t",   "ext4", "-b", "4096", fs_blkdev.c_str(),
             size_str.c_str(),     nullptr};
 
-    rc = logwrap_fork_execvp(arraysize(mke2fs_args), mke2fs_args, nullptr, false, LOG_KLOG, true,
-                             nullptr);
+    rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), NULL,
+                                 true, LOG_KLOG, true, nullptr, nullptr, 0);
     if (rc) {
         LERROR << "mke2fs returned " << rc;
         return rc;
@@ -86,8 +86,8 @@
     const char* const e2fsdroid_args[] = {
             "/system/bin/e2fsdroid", "-e", "-a", fs_mnt_point.c_str(), fs_blkdev.c_str(), nullptr};
 
-    rc = logwrap_fork_execvp(arraysize(e2fsdroid_args), e2fsdroid_args, nullptr, false, LOG_KLOG,
-                             true, nullptr);
+    rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
+                                 NULL, true, LOG_KLOG, true, nullptr, nullptr, 0);
     if (rc) {
         LERROR << "e2fsdroid returned " << rc;
     }
@@ -119,7 +119,8 @@
     };
     // clang-format on
 
-    return logwrap_fork_execvp(arraysize(args), args, nullptr, false, LOG_KLOG, true, nullptr);
+    return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
+                                   LOG_KLOG, true, nullptr, nullptr, 0);
 }
 
 int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index d216458..22b0585 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -36,7 +36,6 @@
 
 #include "fs_mgr_priv.h"
 
-using android::base::EndsWith;
 using android::base::ParseByteCount;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
@@ -47,7 +46,7 @@
 namespace fs_mgr {
 namespace {
 
-constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
+const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
 
 struct FlagList {
     const char *name;
@@ -99,9 +98,58 @@
     return false;
 }
 
+const std::array<const char*, 3> kFileContentsEncryptionMode = {
+        "aes-256-xts",
+        "adiantum",
+        "ice",
+};
+
+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;
-    entry->encryption_options = arg;
+
+    auto parts = Split(arg, ":");
+    if (parts.empty() || parts.size() > 2) {
+        LWARNING << "Warning: fileencryption= flag malformed: " << arg;
+        return;
+    }
+
+    // Alias for backwards compatibility.
+    if (parts[0] == "software") {
+        parts[0] = "aes-256-xts";
+    }
+
+    if (std::find(kFileContentsEncryptionMode.begin(), kFileContentsEncryptionMode.end(),
+                  parts[0]) == kFileContentsEncryptionMode.end()) {
+        LWARNING << "fileencryption= flag malformed, file contents encryption mode not found: "
+                 << arg;
+        return;
+    }
+
+    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;
+        }
+
+        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";
+    }
 }
 
 bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
@@ -123,18 +171,6 @@
                 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);
@@ -226,6 +262,10 @@
                     LWARNING << "Warning: zramsize= flag malformed: " << arg;
                 }
             }
+        } else if (StartsWith(flag, "verify=")) {
+            // If the verify flag is followed by an = and the location for the verity state.
+            entry->fs_mgr_flags.verify = true;
+            entry->verity_loc = 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;
@@ -237,7 +277,8 @@
             // return it.
             entry->fs_mgr_flags.force_fde_or_fbe = true;
             entry->key_loc = arg;
-            entry->encryption_options = "aes-256-xts:aes-256-cts";
+            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;
@@ -549,7 +590,7 @@
     return boot_devices;
 }
 
-FstabEntry BuildDsuUserdataFstabEntry() {
+FstabEntry BuildGsiUserdataFstabEntry() {
     constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
 
     FstabEntry userdata = {
@@ -578,12 +619,7 @@
     return false;
 }
 
-}  // namespace
-
-void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions) {
-    static constexpr char kGsiKeys[] =
-            "/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey";
-    // Convert userdata
+void TransformFstabForGsi(Fstab* fstab) {
     // Inherit fstab properties for userdata.
     FstabEntry userdata;
     if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
@@ -595,77 +631,20 @@
             userdata.key_dir += "/gsi";
         }
     } else {
-        userdata = BuildDsuUserdataFstabEntry();
+        userdata = BuildGsiUserdataFstabEntry();
+    }
+
+    if (EraseFstabEntry(fstab, "/system")) {
+        fstab->emplace_back(BuildGsiSystemFstabEntry());
     }
 
     if (EraseFstabEntry(fstab, "/data")) {
         fstab->emplace_back(userdata);
     }
-
-    // Convert others
-    for (auto&& partition : dsu_partitions) {
-        if (!EndsWith(partition, gsi::kDsuPostfix)) {
-            continue;
-        }
-        // userdata has been handled
-        if (StartsWith(partition, "user")) {
-            continue;
-        }
-        // dsu_partition_name = corresponding_partition_name + kDsuPostfix
-        // e.g.
-        //    system_gsi for system
-        //    product_gsi for product
-        //    vendor_gsi for vendor
-        std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
-        std::string mount_point = "/" + lp_name;
-        std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
-        if (entries.empty()) {
-            FstabEntry entry = {
-                    .blk_device = partition,
-                    // .logical_partition_name is required to look up AVB Hashtree descriptors.
-                    .logical_partition_name = "system",
-                    .mount_point = mount_point,
-                    .fs_type = "ext4",
-                    .flags = MS_RDONLY,
-                    .fs_options = "barrier=1",
-                    .avb_keys = kGsiKeys,
-            };
-            entry.fs_mgr_flags.wait = true;
-            entry.fs_mgr_flags.logical = true;
-            entry.fs_mgr_flags.first_stage_mount = true;
-            // Use the system key which may be in the vbmeta or vbmeta_system
-            // TODO: b/141284191
-            entry.vbmeta_partition = "vbmeta";
-            fstab->emplace_back(entry);
-            entry.vbmeta_partition = "vbmeta_system";
-            fstab->emplace_back(entry);
-        } else {
-            // If the corresponding partition exists, transform all its Fstab
-            // by pointing .blk_device to the DSU partition.
-            for (auto&& entry : entries) {
-                entry->blk_device = partition;
-                if (entry->avb_keys.size() > 0) {
-                    entry->avb_keys += ":";
-                }
-                // If the DSU is signed by OEM, the original Fstab already has the information
-                // required by avb, otherwise the DSU is GSI and will need the avb_keys as listed
-                // below.
-                entry->avb_keys += kGsiKeys;
-            }
-            // Make sure the ext4 is included to support GSI.
-            auto partition_ext4 =
-                    std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
-                        return entry.mount_point == mount_point && entry.fs_type == "ext4";
-                    });
-            if (partition_ext4 == fstab->end()) {
-                auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
-                new_entry.fs_type = "ext4";
-                fstab->emplace_back(new_entry);
-            }
-        }
-    }
 }
 
+}  // 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) {
@@ -680,9 +659,7 @@
         return false;
     }
     if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
-        std::string lp_names;
-        ReadFileToString(gsi::kGsiLpNamesFile, &lp_names);
-        TransformFstabForDsu(fstab, Split(lp_names, ","));
+        TransformFstabForGsi(fstab);
     }
 
     SkipMountingPartitions(fstab);
@@ -719,14 +696,13 @@
     return true;
 }
 
-// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
-// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
-// device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
-// only common files for all targets can be put into system partition. It is under
-// /system/system_ext because GSI is a single system.img that includes the contents of system_ext
-// partition and product partition under /system/system_ext and /system/product, respectively.
+// 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/system_ext/etc/init/config/skip_mount.cfg";
+    constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
 
     std::string skip_config;
     auto save_errno = errno;
@@ -743,7 +719,6 @@
                                  [&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;
     }
@@ -794,21 +769,6 @@
     return nullptr;
 }
 
-std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
-    std::vector<FstabEntry*> entries;
-    if (fstab == nullptr) {
-        return entries;
-    }
-
-    for (auto& entry : *fstab) {
-        if (entry.mount_point == path) {
-            entries.emplace_back(&entry);
-        }
-    }
-
-    return entries;
-}
-
 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";
@@ -828,14 +788,29 @@
     return ExtraBootDevices(fstab);
 }
 
+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/q-developer-gsi.avbpubkey:"
+                                 "/avb/r-developer-gsi.avbpubkey:/avb/s-developer-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;
+}
+
 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";
+        // In AVB, the dm device name is vroot instead of system.
+        base_device = entry.fs_mgr_flags.avb ? "vroot" : "system";
     } else {
         base_device = android::base::Basename(entry.mount_point);
     }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 0579a3d..f21d529 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -43,7 +43,6 @@
 #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>
@@ -133,7 +132,7 @@
     return ret | !rmdir(test_directory.c_str());
 }
 
-// At less than 1% or 8MB of free space return value of false,
+// 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
@@ -145,36 +144,9 @@
         return true;
     }
 
-    static constexpr int kPercentThreshold = 1;                       // 1%
-    static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024;  // 8MB
+    static constexpr int kPercentThreshold = 1;  // 1%
 
-    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
-           (vst.f_bfree * vst.f_bsize) >= kSizeThreshold;
-}
-
-const auto kPhysicalDevice = "/dev/block/by-name/"s;
-
-bool fs_mgr_update_blk_device(FstabEntry* entry) {
-    if (entry->fs_mgr_flags.logical) {
-        fs_mgr_update_logical_partition(entry);
-    }
-    if (fs_mgr_access(entry->blk_device)) {
-        return true;
-    }
-    if (entry->blk_device != "/dev/root") {
-        return false;
-    }
-
-    // special case for system-as-root (taimen and others)
-    auto blk_device = kPhysicalDevice + "system";
-    if (!fs_mgr_access(blk_device)) {
-        blk_device += fs_mgr_get_slot_suffix();
-        if (!fs_mgr_access(blk_device)) {
-            return false;
-        }
-    }
-    entry->blk_device = blk_device;
-    return true;
+    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
 }
 
 bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
@@ -184,19 +156,19 @@
     if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
         return true;
     }
-
-    // blk_device needs to be setup so we can check superblock.
-    // If we fail here, because during init first stage and have doubts.
-    if (!fs_mgr_update_blk_device(entry)) {
-        return true;
+    if (entry->fs_mgr_flags.logical) {
+        fs_mgr_update_logical_partition(entry);
     }
-
-    // check if ext4 de-dupe
     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;
 }
@@ -415,6 +387,8 @@
     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);
 }
@@ -469,7 +443,7 @@
     auto metadata = builder->Export();
     if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
         if (change) *change = true;
-        if (!DestroyLogicalPartition(partition_name)) return false;
+        if (!DestroyLogicalPartition(partition_name, 0s)) return false;
     } else {
         LERROR << "delete partition " << overlay;
         return false;
@@ -713,7 +687,7 @@
     }
     report = report + ")=";
 
-    auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
+    auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
                      options.c_str());
     if (ret) {
         retval = false;
@@ -776,13 +750,12 @@
     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_NOATIME;
+    entry.flags = MS_RELATIME;
     if (readonly) {
         entry.flags |= MS_RDONLY;
     } else {
         fs_mgr_set_blk_ro(device_path, false);
     }
-    entry.fs_mgr_flags.check = true;
     auto save_errno = errno;
     auto mounted = fs_mgr_do_mount_one(entry) == 0;
     if (!mounted) {
@@ -860,25 +833,6 @@
     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)) {
-                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();
@@ -925,26 +879,17 @@
             // 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)) {
+            if (builder->GetBlockDeviceInfo(partition_name, &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;
-                    }
+                    LERROR << "resize " << partition_name;
+                    return false;
                 }
-                if (!partition_create) DestroyLogicalPartition(partition_name);
+                if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
                 changed = true;
                 *partition_exists = false;
             }
@@ -962,16 +907,9 @@
     }
 
     if (changed || partition_create) {
-        CreateLogicalPartitionParams params = {
-                .block_device = super_device,
-                .metadata_slot = slot_number,
-                .partition_name = partition_name,
-                .force_writable = true,
-                .timeout_ms = 10s,
-        };
-        if (!CreateLogicalPartition(params, scratch_device)) {
+        if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
+                                    scratch_device))
             return false;
-        }
 
         if (change) *change = true;
     }
@@ -1082,7 +1020,7 @@
             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)) {
+                fs_mgr_wait_for_file(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 */)) {
@@ -1192,15 +1130,11 @@
     if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         auto scratch_device = fs_mgr_overlayfs_scratch_device();
         if (scratch_device.empty()) {
-            auto metadata_slot = fs_mgr_overlayfs_slot_number();
-            CreateLogicalPartitionParams params = {
-                    .block_device = fs_mgr_overlayfs_super_device(metadata_slot),
-                    .metadata_slot = metadata_slot,
-                    .partition_name = android::base::Basename(kScratchMountPoint),
-                    .force_writable = true,
-                    .timeout_ms = 10s,
-            };
-            CreateLogicalPartition(params, &scratch_device);
+            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());
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index c5e477c..70abf5b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -88,19 +88,26 @@
 
 using namespace std::chrono_literals;
 
+enum class FileWaitMode { Exists, DoesNotExist };
+
+bool fs_mgr_wait_for_file(const std::string& filename,
+                          const std::chrono::milliseconds relative_timeout,
+                          FileWaitMode wait_mode = FileWaitMode::Exists);
+
 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(const android::fs_mgr::FstabEntry& entry, int* mode);
 
 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 fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab, bool wait);
 
 namespace android {
 namespace fs_mgr {
-bool UnmapDevice(const std::string& name);
+bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 93bba68..967631b 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -82,28 +82,36 @@
 
 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";
+[[noreturn]] void reboot(bool dedupe) {
+    if (dedupe) {
+        LOG(INFO) << "The device will now reboot to recovery and attempt un-deduplication.";
     } else {
         LOG(INFO) << "Successfully disabled verity\nrebooting device";
     }
     ::sync();
-    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,remount");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, dedupe ? "reboot,recovery" : "reboot,remount");
     ::sleep(60);
     ::exit(0);  // SUCCESS
 }
 
 }  // namespace
 
-static int do_remount(int argc, char* argv[]) {
+int main(int argc, char* argv[]) {
+    android::base::InitLogging(argv, MyLogger);
+
     enum {
-        SUCCESS = 0,
+        SUCCESS,
         NOT_USERDEBUG,
         BADARG,
         NOT_ROOT,
@@ -157,7 +165,7 @@
 
     // Make sure we are root.
     if (::getuid() != 0) {
-        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+        LOG(ERROR) << "must be run as root";
         return NOT_ROOT;
     }
 
@@ -241,38 +249,48 @@
 
     // Check verity and optionally setup overlayfs backing.
     auto reboot_later = false;
-    auto user_please_reboot_later = false;
-    auto setup_overlayfs = false;
+    auto uses_overlayfs = fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported;
     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(
+                    auto 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(WARNING) << "Disabling verity for " << mount_point;
+                        just_disabled_verity = true;
+                        reboot_later = can_reboot;
+                        if (reboot_later) {
+                            // w/o overlayfs available, also check for dedupe
+                            if (!uses_overlayfs) {
+                                ++it;
+                                continue;
+                            }
+                            reboot(false);
+                        }
+                    } else if (fs_mgr_set_blk_ro(entry.blk_device, false)) {
+                        fec::io fh(entry.blk_device.c_str(), O_RDWR);
+                        if (fh && fh.set_verity_status(false)) {
+                            LOG(WARNING) << "Disabling verity for " << mount_point;
+                            just_disabled_verity = true;
+                            reboot_later = can_reboot;
+                            if (reboot_later && !uses_overlayfs) {
+                                ++it;
+                                continue;
+                            }
+                        }
+                    }
                 }
             }
-            if (!ret) {
-                LOG(ERROR) << "Skipping " << mount_point << " for remount";
-                it = partitions.erase(it);
-                continue;
-            }
+            LOG(ERROR) << "Skipping " << mount_point;
+            it = partitions.erase(it);
+            continue;
         }
 
         auto change = false;
@@ -280,9 +298,6 @@
         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";
@@ -293,12 +308,8 @@
         ++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;
-        }
+    if (partitions.empty()) {
+        if (reboot_later) reboot(false);
         LOG(WARNING) << "No partitions to remount";
         return retval;
     }
@@ -374,18 +385,7 @@
         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;
-    }
+    if (reboot_later) reboot(false);
 
     return retval;
 }
-
-int main(int argc, char* argv[]) {
-    android::base::InitLogging(argv, MyLogger);
-    int result = do_remount(argc, argv);
-    printf("remount %s\n", result ? "failed" : "succeeded");
-    return result;
-}
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index 1e65587..58ef9b6 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -101,9 +101,7 @@
         }
     }
 
-    const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
-
-    auto mounted = GetMountState(mount_point);
+    auto mounted = GetMountState(rec->mount_point);
     if (mounted == MountState::ERROR) {
         return false;
     }
@@ -111,6 +109,8 @@
         return true;
     }
 
+    const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;
+
     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;
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index efa2180..3f09157 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -35,7 +35,6 @@
 #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>
@@ -276,6 +275,248 @@
     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)
+{
+    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;
+    }
+
+    auto fp = std::unique_ptr<FILE, decltype(&fclose)>{fopen(fname, "re+"), fclose};
+
+    if (!fp) {
+        PERROR << "Failed to open " << fname;
+        return -1;
+    }
+
+    /* check magic */
+    if (fseek(fp.get(), start, SEEK_SET) < 0 || fread(&magic, sizeof(magic), 1, fp.get()) != 1) {
+        PERROR << "Failed to read magic from " << fname;
+        return -1;
+    }
+
+    if (magic != METADATA_MAGIC) {
+        magic = METADATA_MAGIC;
+
+        if (fseek(fp.get(), start, SEEK_SET) < 0 ||
+            fwrite(&magic, sizeof(magic), 1, fp.get()) != 1) {
+            PERROR << "Failed to write magic to " << fname;
+            return -1;
+        }
+
+        rc = metadata_add(fp.get(), start + sizeof(magic), stag, slength, offset);
+        if (rc < 0) {
+            PERROR << "Failed to add metadata to " << fname;
+        }
+
+        return rc;
+    }
+
+    start += sizeof(magic);
+
+    while (1) {
+        n = fscanf(fp.get(), "%" STRINGIFY(METADATA_TAG_MAX_LENGTH) "s %u\n", tag, &length);
+
+        if (n == 2 && strcmp(tag, METADATA_EOD)) {
+            /* found a tag */
+            start = ftell(fp.get());
+
+            if (!strcmp(tag, stag) && length == slength) {
+                *offset = start;
+                return 0;
+            }
+
+            start += length;
+
+            if (fseek(fp.get(), length, SEEK_CUR) < 0) {
+                PERROR << "Failed to seek " << fname;
+                return -1;
+            }
+        } else {
+            rc = metadata_add(fp.get(), start, stag, slength, offset);
+            if (rc < 0) {
+                PERROR << "Failed to write metadata to " << fname;
+            }
+            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];
@@ -299,23 +540,119 @@
     return 0;
 }
 
-bool fs_mgr_load_verity_state(int* mode) {
+static int compare_last_signature(const FstabEntry& entry, 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, entry.blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) ==
+        -1) {
+        PERROR << "Failed to open '" << entry.blk_device << "'";
+        return rc;
+    }
+
+    // read verity metadata
+    if (fec_verity_get_metadata(f, &verity) == -1) {
+        PERROR << "Failed to get verity metadata '" << entry.blk_device << "'";
+        goto out;
+    }
+
+    SHA256(verity.signature, sizeof(verity.signature), curr);
+
+    if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s", basename(entry.mount_point.c_str())) >=
+        (int)sizeof(tag)) {
+        LERROR << "Metadata tag name too long for " << entry.mount_point;
+        goto out;
+    }
+
+    if (metadata_find(entry.verity_loc.c_str(), tag, SHA256_DIGEST_LENGTH, &offset) < 0) {
+        goto out;
+    }
+
+    fd = TEMP_FAILURE_RETRY(open(entry.verity_loc.c_str(), O_RDWR | O_SYNC | O_CLOEXEC));
+
+    if (fd == -1) {
+        PERROR << "Failed to open " << entry.verity_loc;
+        goto out;
+    }
+
+    if (TEMP_FAILURE_RETRY(pread64(fd, prev, sizeof(prev), offset)) != sizeof(prev)) {
+        PERROR << "Failed to read " << sizeof(prev) << " bytes from " << entry.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 " << entry.verity_loc
+                   << " offset " << offset;
+            goto out;
+        }
+    }
+
+    rc = 0;
+
+out:
+    fec_close(f);
+    return rc;
+}
+
+static int get_verity_state_offset(const FstabEntry& entry, off64_t* offset) {
+    char tag[METADATA_TAG_MAX_LENGTH + 1];
+
+    if (snprintf(tag, sizeof(tag), VERITY_STATE_TAG "_%s", basename(entry.mount_point.c_str())) >=
+        (int)sizeof(tag)) {
+        LERROR << "Metadata tag name too long for " << entry.mount_point;
+        return -1;
+    }
+
+    return metadata_find(entry.verity_loc.c_str(), tag, sizeof(struct verity_state), offset);
+}
+
+int load_verity_state(const FstabEntry& entry, int* mode) {
     // unless otherwise specified, use EIO mode.
     *mode = VERITY_MODE_EIO;
 
-    // The bootloader communicates verity mode via the kernel commandline
-    std::string verity_mode;
-    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
-        return false;
+    // 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;
     }
 
-    if (verity_mode == "enforcing") {
+    off64_t offset = 0;
+    if (get_verity_state_offset(entry, &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(entry.verity_loc.c_str(), offset, *mode);
+    }
+
+    int match = 0;
+    if (!compare_last_signature(entry, &match) && !match) {
+        /* partition has been reflashed, reset dm-verity state */
         *mode = VERITY_MODE_DEFAULT;
-    } else if (verity_mode == "logging") {
-        *mode = VERITY_MODE_LOGGING;
+        return write_verity_state(entry.verity_loc.c_str(), offset, *mode);
     }
 
-    return true;
+    return read_verity_state(entry.verity_loc.c_str(), offset, mode);
 }
 
 // Update the verity table using the actual block device path.
@@ -422,7 +759,7 @@
 
     params.ecc_dev = entry->blk_device.c_str();
 
-    if (!fs_mgr_load_verity_state(&params.mode)) {
+    if (load_verity_state(*entry, &params.mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
          * restart loop, and no corrupted data will be exposed to userspace
@@ -530,7 +867,7 @@
     }
 
     // make sure we've set everything up properly
-    if (wait_for_verity_dev && !WaitForFile(entry->blk_device, 1s)) {
+    if (wait_for_verity_dev && !fs_mgr_wait_for_file(entry->blk_device, 1s)) {
         goto out;
     }
 
@@ -547,9 +884,9 @@
     return retval;
 }
 
-bool fs_mgr_teardown_verity(FstabEntry* entry) {
+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)) {
+    if (!android::fs_mgr::UnmapDevice(mount_point, wait ? 1000ms : 0ms)) {
         return false;
     }
     LINFO << "Unmapped verity device " << mount_point;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index ca67f37..bdec7be 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -105,8 +105,6 @@
 // it destroys verity devices from device mapper after the device is unmounted.
 int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
 
-int fs_mgr_remount_userdata_into_checkpointing(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
deleted file mode 100644
index 74d160e..0000000
--- a/fs_mgr/include/fs_mgr/file_wait.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index f3d09fb..f33fc02 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -29,7 +29,6 @@
 
 #include <chrono>
 #include <memory>
-#include <optional>
 #include <string>
 #include <vector>
 
@@ -50,62 +49,26 @@
 // method for ReadMetadata and CreateLogicalPartitions.
 bool CreateLogicalPartitions(const std::string& block_device);
 
-struct CreateLogicalPartitionParams {
-    // Block device of the super partition.
-    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);
 
-    // If |metadata| is null, the slot will be read using |metadata_slot|.
-    const LpMetadata* metadata = nullptr;
-    std::optional<uint32_t> metadata_slot;
-
-    // If |partition| is not set, it will be found via |partition_name|.
-    const LpMetadataPartition* partition = nullptr;
-    std::string partition_name;
-
-    // Force the device to be read-write even if it was specified as readonly
-    // in the metadata.
-    bool force_writable = false;
-
-    // If |timeout_ms| is non-zero, then CreateLogicalPartition will block for
-    // the given amount of time until the path returned in |path| is available.
-    std::chrono::milliseconds timeout_ms = {};
-
-    // If this is non-empty, it will override the device mapper name (by
-    // default the partition name will be used).
-    std::string device_name;
-
-    // If non-null, this will use the specified IPartitionOpener rather than
-    // the default one.
-    const IPartitionOpener* partition_opener = nullptr;
-
-    // Helpers for determining the effective partition and device name.
-    std::string GetPartitionName() const;
-    std::string GetDeviceName() const;
-
-    // Specify ownership of fields. The ownership of these fields are managed
-    // by the caller of InitDefaults().
-    // These are not declared in CreateLogicalPartitionParams so that the
-    // copy constructor is not deleted.
-    struct OwnedData {
-        std::unique_ptr<LpMetadata> metadata;
-        std::unique_ptr<IPartitionOpener> partition_opener;
-    };
-
-    // Fill in default values for |params| that CreateLogicalPartition assumes. Caller does
-    // not need to call this before calling CreateLogicalPartition; CreateLogicalPartition sets
-    // values when they are missing.
-    // Caller is responsible for destroying owned_data when |this| is not used.
-    bool InitDefaults(OwnedData* owned);
-};
-
-bool CreateLogicalPartition(CreateLogicalPartitionParams params, 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);
-
-// Helper for populating a DmTable for a logical partition.
-bool CreateDmTable(CreateLogicalPartitionParams params, android::dm::DmTable* table);
+bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index c6a16e3..d7afed6 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,6 +38,7 @@
     std::string fs_options;
     std::string key_loc;
     std::string key_dir;
+    std::string verity_loc;
     off64_t length = 0;
     std::string label;
     int partnum = -1;
@@ -45,7 +46,8 @@
     int max_comp_streams = 0;
     off64_t zram_size = 0;
     off64_t reserved_size = 0;
-    std::string encryption_options;
+    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;
@@ -100,18 +102,9 @@
 bool SkipMountingPartitions(Fstab* fstab);
 
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
-// The Fstab can contain multiple entries for the same mount point with different configurations.
-std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
 
-// This method builds DSU fstab entries and transfer the fstab.
-//
-// fstab points to the unmodified fstab.
-//
-// dsu_partitions contains partition names, e.g.
-//     dsu_partitions[0] = "system_gsi"
-//     dsu_partitions[1] = "userdata_gsi"
-//     dsu_partitions[2] = ...
-void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions);
+// Helper method to build a GSI fstab entry for mounting /system.
+FstabEntry BuildGsiSystemFstabEntry();
 
 std::set<std::string> GetBootDevices();
 
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 8a924d5..c8c2d83 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -27,12 +27,8 @@
         "dm_target.cpp",
         "dm.cpp",
         "loop_control.cpp",
-        "utility.cpp",
     ],
 
-    static_libs: [
-        "libext2_uuid",
-    ],
     header_libs: [
         "libbase_headers",
         "liblog_headers",
@@ -44,54 +40,17 @@
     },
 }
 
-filegroup {
-    name: "libdm_test_srcs",
-    srcs: [
-        "dm_test.cpp",
-        "loop_control_test.cpp",
-        "test_util.cpp",
-    ],
-}
-
-cc_defaults {
-    name: "libdm_defaults",
+cc_test {
+    name: "libdm_test",
     defaults: ["fs_mgr_defaults"],
     static_libs: [
         "libdm",
         "libbase",
-        "libext2_uuid",
-        "libfs_mgr",
         "liblog",
     ],
-    srcs: [":libdm_test_srcs"],
-}
-
-cc_test {
-    name: "libdm_test",
-    defaults: ["libdm_defaults"],
-}
-
-cc_test {
-    name: "vts_libdm_test",
-    defaults: ["libdm_defaults"],
-    test_suites: ["vts-core"],
-    auto_gen_config: true,
-    require_root: true,
-    test_min_api_level: 29,
-}
-
-cc_fuzz {
-  name: "dm_linear_table_fuzzer",
-  defaults: ["fs_mgr_defaults"],
-  srcs: [
-    "dm_linear_fuzzer.cpp",
-    "test_util.cpp",
-  ],
-  static_libs: [
-        "libdm",
-        "libbase",
-        "libext2_uuid",
-        "libfs_mgr",
-        "liblog",
-  ],
+    srcs: [
+        "dm_test.cpp",
+        "loop_control_test.cpp",
+        "test_util.cpp",
+    ]
 }
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index e7a3ff2..a4614d0 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,22 +20,12 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 
-#include <functional>
-#include <thread>
-
-#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
-#include <android-base/strings.h>
-#include <uuid/uuid.h>
-
-#include "utility.h"
 
 namespace android {
 namespace dm {
 
-using namespace std::literals;
-
 DeviceMapper::DeviceMapper() : fd_(-1) {
     fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
     if (fd_ < 0) {
@@ -47,13 +37,13 @@
     static DeviceMapper instance;
     return instance;
 }
-
 // Creates a new device mapper device
-bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
+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;
@@ -61,9 +51,6 @@
 
     struct dm_ioctl io;
     InitIo(&io, name);
-    if (!uuid.empty()) {
-        snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
-    }
 
     if (ioctl(fd_, DM_DEV_CREATE, &io)) {
         PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
@@ -79,14 +66,17 @@
     return true;
 }
 
-bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
-    if (GetState(name) == DmDeviceState::INVALID) {
-        return true;
-    }
-    return DeleteDevice(name);
-}
-
 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);
 
@@ -103,67 +93,9 @@
     return true;
 }
 
-static std::string GenerateUuid() {
-    uuid_t uuid_bytes;
-    uuid_generate(uuid_bytes);
-
-    char uuid_chars[37] = {};
-    uuid_unparse_lower(uuid_bytes, uuid_chars);
-
-    return std::string{uuid_chars};
-}
-
-bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
-                                const std::chrono::milliseconds& timeout_ms) {
-    std::string uuid = GenerateUuid();
-    if (!CreateDevice(name, uuid)) {
-        return false;
-    }
-
-    // We use the unique path for testing whether the device is ready. After
-    // that, it's safe to use the dm-N path which is compatible with callers
-    // that expect it to be formatted as such.
-    std::string unique_path;
-    if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
-        !GetDmDevicePathByName(name, path)) {
-        DeleteDevice(name);
-        return false;
-    }
-
-    if (timeout_ms <= std::chrono::milliseconds::zero()) {
-        return true;
-    }
-    if (!WaitForFile(unique_path, timeout_ms)) {
-        LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
-        DeleteDevice(name);
-        return false;
-    }
-    return true;
-}
-
-bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
-    struct dm_ioctl io;
-    InitIo(&io, name);
-    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
-        PLOG(ERROR) << "Failed to get device path: " << name;
-        return false;
-    }
-
-    if (io.uuid[0] == '\0') {
-        LOG(ERROR) << "Device does not have a unique path: " << name;
-        return false;
-    }
-    *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
-    return true;
-}
-
-std::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {
-    struct dm_ioctl io;
-    InitIo(&io, name);
-    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
-        return std::nullopt;
-    }
-    return Info(io.flags);
+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 {
@@ -178,27 +110,12 @@
     return DmDeviceState::SUSPENDED;
 }
 
-bool DeviceMapper::ChangeState(const std::string& name, DmDeviceState state) {
-    if (state != DmDeviceState::SUSPENDED && state != DmDeviceState::ACTIVE) {
-        return false;
-    }
-
-    struct dm_ioctl io;
-    InitIo(&io, name);
-
-    if (state == DmDeviceState::SUSPENDED) io.flags = DM_SUSPEND_FLAG;
-
-    if (ioctl(fd_, DM_DEV_SUSPEND, &io) < 0) {
-        PLOG(ERROR) << "DM_DEV_SUSPEND "
-                    << (state == DmDeviceState::SUSPENDED ? "suspend" : "resume") << " failed";
-        return false;
-    }
-    return true;
-}
-
 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
-    std::string ignore_path;
-    if (!CreateDevice(name, table, &ignore_path, 0ms)) {
+    if (!CreateDevice(name)) {
+        return false;
+    }
+    if (!LoadTableAndActivate(name, table)) {
+        DeleteDevice(name);
         return false;
     }
     return true;
@@ -293,20 +210,6 @@
     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();
 
@@ -385,26 +288,6 @@
     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);
 }
@@ -471,13 +354,5 @@
     }
 }
 
-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_linear_fuzzer.cpp b/fs_mgr/libdm/dm_linear_fuzzer.cpp
deleted file mode 100644
index 8462901..0000000
--- a/fs_mgr/libdm/dm_linear_fuzzer.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * 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 <stddef.h>
-#include <stdint.h>
-#include <string.h>
-
-#include <chrono>
-
-#include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm_table.h>
-#include <libdm/loop_control.h>
-
-#include "test_util.h"
-
-using namespace android;
-using namespace android::base;
-using namespace android::dm;
-using namespace std;
-using namespace std::chrono_literals;
-
-/*
- * This test aims at making the library crash, so these functions are not
- * really useful.
- * Keeping them here for future use.
- */
-template <class T, class C>
-void ASSERT_EQ(const T& /*a*/, const C& /*b*/) {
-    // if (a != b) {}
-}
-
-template <class T>
-void ASSERT_FALSE(const T& /*a*/) {
-    // if (a) {}
-}
-
-template <class T, class C>
-void ASSERT_GE(const T& /*a*/, const C& /*b*/) {
-    // if (a < b) {}
-}
-
-template <class T, class C>
-void ASSERT_NE(const T& /*a*/, const C& /*b*/) {
-    // if (a == b) {}
-}
-
-template <class T>
-void ASSERT_TRUE(const T& /*a*/) {
-    // if (!a) {}
-}
-
-template <class T, class C>
-void EXPECT_EQ(const T& a, const C& b) {
-    ASSERT_EQ(a, b);
-}
-
-template <class T>
-void EXPECT_TRUE(const T& a) {
-    ASSERT_TRUE(a);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    uint64_t val[6];
-
-    if (size != sizeof(val)) {
-        return 0;
-    }
-
-    memcpy(&val, &data[0], sizeof(*val));
-
-    unique_fd tmp1(CreateTempFile("file_1", 4096));
-    ASSERT_GE(tmp1, 0);
-    unique_fd tmp2(CreateTempFile("file_2", 4096));
-    ASSERT_GE(tmp2, 0);
-
-    LoopDevice loop_a(tmp1, 10s);
-    ASSERT_TRUE(loop_a.valid());
-    LoopDevice loop_b(tmp2, 10s);
-    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>(val[0], val[1], loop_a.device(), val[2]));
-    ASSERT_TRUE(table.Emplace<DmTargetLinear>(val[3], val[4], loop_b.device(), val[5]));
-    ASSERT_TRUE(table.valid());
-    ASSERT_EQ(2u, table.num_sectors());
-
-    TempDevice dev("libdm-test-dm-linear", table);
-    ASSERT_TRUE(dev.valid());
-    ASSERT_FALSE(dev.path().empty());
-
-    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());
-
-    // 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());
-
-    return 0;
-}
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
index efe03ab..15c7ce1 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -26,7 +26,6 @@
     if (!target->Valid()) {
         return false;
     }
-    num_sectors_ += target->size();
     targets_.push_back(std::move(target));
     return true;
 }
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 65f6e12..cb33eea 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -16,13 +16,8 @@
 
 #include "libdm/dm_target.h"
 
-#include <inttypes.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>
@@ -120,133 +115,5 @@
     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_);
-}
-
-// Computes the percentage of complition for snapshot status.
-// @sectors_initial is the number of sectors_allocated stored right before
-// starting the merge.
-double DmTargetSnapshot::MergePercent(const DmTargetSnapshot::Status& status,
-                                      uint64_t sectors_initial) {
-    uint64_t s = status.sectors_allocated;
-    uint64_t t = status.total_sectors;
-    uint64_t m = status.metadata_sectors;
-    uint64_t i = sectors_initial == 0 ? t : sectors_initial;
-
-    if (t <= s || i <= s) {
-        return 0.0;
-    }
-    if (s == 0 || t == 0 || s <= m) {
-        return 100.0;
-    }
-    return 100.0 / (i - m) * (i - s);
-}
-
-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) {
-    // Try to parse the line as it should be
-    int args = sscanf(text.c_str(), "%" PRIu64 "/%" PRIu64 " %" PRIu64, &status->sectors_allocated,
-                      &status->total_sectors, &status->metadata_sectors);
-    if (args == 3) {
-        return true;
-    }
-    auto sections = android::base::Split(text, " ");
-    if (sections.size() == 0) {
-        LOG(ERROR) << "could not parse empty status";
-        return false;
-    }
-    // Error codes are: "Invalid", "Overflow" and "Merge failed"
-    if (sections.size() == 1) {
-        if (text == "Invalid" || text == "Overflow") {
-            status->error = text;
-            return true;
-        }
-    } else if (sections.size() == 2 && text == "Merge failed") {
-        status->error = text;
-        return true;
-    }
-    LOG(ERROR) << "could not parse snapshot status: wrong format";
-    return false;
-}
-
-bool DmTargetSnapshot::GetDevicesFromParams(const std::string& params, std::string* base_device,
-                                            std::string* cow_device) {
-    auto pieces = android::base::Split(params, " ");
-    if (pieces.size() < 2) {
-        LOG(ERROR) << "Parameter string is invalid: " << params;
-        return false;
-    }
-    *base_device = pieces[0];
-    *cow_device = pieces[1];
-    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_) +
-           (set_dun_ ? " 1 set_dun" : "");
-}
-
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index ed2fa83..70823c6 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -24,7 +24,6 @@
 
 #include <chrono>
 #include <ctime>
-#include <iostream>
 #include <map>
 #include <thread>
 
@@ -36,17 +35,88 @@
 #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));
+    vector<DmTargetTypeInfo> targets;
+    ASSERT_TRUE(dm.GetAvailableTargets(&targets));
+
+    map<string, DmTargetTypeInfo> by_name;
+    for (const auto& target : targets) {
+        by_name[target.name()] = target;
+    }
+
+    auto iter = by_name.find("linear");
+    EXPECT_NE(iter, by_name.end());
 }
 
+// Helper to ensure that device mapper devices are released.
+class TempDevice {
+  public:
+    TempDevice(const std::string& name, const DmTable& table)
+        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+        valid_ = dm_.CreateDevice(name, table);
+    }
+    TempDevice(TempDevice&& other) 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);
@@ -59,32 +129,22 @@
     ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));
     ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));
 
-    LoopDevice loop_a(tmp1, 10s);
+    LoopDevice loop_a(tmp1);
     ASSERT_TRUE(loop_a.valid());
-    LoopDevice loop_b(tmp2, 10s);
+    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.AddTarget(make_unique<DmTargetLinear>(0, 1, loop_a.device(), 0)));
+    ASSERT_TRUE(table.AddTarget(make_unique<DmTargetLinear>(1, 1, loop_b.device(), 0)));
     ASSERT_TRUE(table.valid());
-    ASSERT_EQ(2u, table.num_sectors());
 
     TempDevice dev("libdm-test-dm-linear", table);
     ASSERT_TRUE(dev.valid());
     ASSERT_FALSE(dev.path().empty());
-
-    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());
+    ASSERT_TRUE(dev.WaitForUdev());
 
     // Note: a scope is needed to ensure that there are no open descriptors
     // when we go to close the device.
@@ -102,6 +162,7 @@
     }
 
     // Test GetTableStatus.
+    DeviceMapper& dm = DeviceMapper::Instance();
     vector<DeviceMapper::TargetInfo> targets;
     ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
     ASSERT_EQ(targets.size(), 2);
@@ -114,44 +175,11 @@
     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, DmSuspendResume) {
-    unique_fd tmp1(CreateTempFile("file_suspend_resume", 512));
-    ASSERT_GE(tmp1, 0);
-
-    LoopDevice loop_a(tmp1, 10s);
-    ASSERT_TRUE(loop_a.valid());
-
-    DmTable table;
-    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));
-    ASSERT_TRUE(table.valid());
-    ASSERT_EQ(1u, table.num_sectors());
-
-    TempDevice dev("libdm-test-dm-suspend-resume", table);
-    ASSERT_TRUE(dev.valid());
-    ASSERT_FALSE(dev.path().empty());
-
-    auto& dm = DeviceMapper::Instance();
-
-    // Test Set and Get status of device.
-    vector<DeviceMapper::TargetInfo> targets;
-    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
-
-    ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::SUSPENDED));
-    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::SUSPENDED);
-
-    ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::ACTIVE));
-    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
-}
-
 TEST(libdm, DmVerityArgsAvb2) {
     std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
     std::string algorithm = "sha1";
@@ -173,350 +201,3 @@
             "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_, 10s);
-    ASSERT_TRUE(base_loop_->valid());
-    cow_loop_ = std::make_unique<LoopDevice>(cow_fd_, 10s);
-    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());
-    ASSERT_EQ(kBaseDeviceSize / kSectorSize, origin_table.num_sectors());
-
-    origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
-    ASSERT_TRUE(origin_dev_->valid());
-    ASSERT_FALSE(origin_dev_->path().empty());
-
-    // 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());
-    ASSERT_EQ(kBaseDeviceSize / kSectorSize, snap_table.num_sectors());
-
-    snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
-    ASSERT_TRUE(snapshot_dev_->valid());
-    ASSERT_FALSE(snapshot_dev_->path().empty());
-
-    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());
-    ASSERT_EQ(kBaseDeviceSize / kSectorSize, merge_table.num_sectors());
-
-    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, ParseStatusText) {
-    DmTargetSnapshot::Status status;
-
-    // Bad inputs
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("X", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123/456", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456 789", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456/789", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123/456/789", &status));
-    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 / 456 789", &status));
-
-    // Good input
-    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("123/456 789", &status));
-    EXPECT_EQ(status.sectors_allocated, 123);
-    EXPECT_EQ(status.total_sectors, 456);
-    EXPECT_EQ(status.metadata_sectors, 789);
-
-    // Known error codes
-    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Invalid", &status));
-    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Merge failed", &status));
-    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Overflow", &status));
-}
-
-TEST(libdm, DmSnapshotMergePercent) {
-    DmTargetSnapshot::Status status;
-
-    // Correct input
-    status.sectors_allocated = 1000;
-    status.total_sectors = 1000;
-    status.metadata_sectors = 0;
-    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 1.0);
-
-    status.sectors_allocated = 500;
-    status.total_sectors = 1000;
-    status.metadata_sectors = 0;
-    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 49.0);
-    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 51.0);
-
-    status.sectors_allocated = 0;
-    status.total_sectors = 1000;
-    status.metadata_sectors = 0;
-    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);
-
-    status.sectors_allocated = 500;
-    status.total_sectors = 1000;
-    status.metadata_sectors = 500;
-    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);
-
-    status.sectors_allocated = 500;
-    status.total_sectors = 1000;
-    status.metadata_sectors = 0;
-    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 500), 1.0);
-    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 1000), 51.0);
-    EXPECT_GE(DmTargetSnapshot::MergePercent(status, 1000), 49.0);
-
-    // Robustness
-    status.sectors_allocated = 2000;
-    status.total_sectors = 1000;
-    status.metadata_sectors = 0;
-    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
-
-    status.sectors_allocated = 2000;
-    status.total_sectors = 1000;
-    status.metadata_sectors = 2000;
-    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
-
-    status.sectors_allocated = 2000;
-    status.total_sectors = 0;
-    status.metadata_sectors = 2000;
-    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
-
-    status.sectors_allocated = 1000;
-    status.total_sectors = 0;
-    status.metadata_sectors = 1000;
-    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 0), 0.0);
-}
-
-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
index e25ce7f..28e6e01 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -20,14 +20,11 @@
 #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 <chrono>
 #include <memory>
-#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -40,15 +37,13 @@
 #define DM_VERSION2 (0)
 
 #define DM_ALIGN_MASK (7)
-#define DM_ALIGN(x) (((x) + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
 
 namespace android {
 namespace dm {
 
 enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
 
-static constexpr uint64_t kSectorSize = 512;
-
 class DeviceMapper final {
   public:
     class DmBlockDevice final {
@@ -73,67 +68,21 @@
         uint64_t dev_;
     };
 
-    class Info {
-        uint32_t flags_;
-
-      public:
-        explicit Info(uint32_t flags) : flags_(flags) {}
-
-        bool IsActiveTablePresent() const { return flags_ & DM_ACTIVE_PRESENT_FLAG; }
-        bool IsBufferFull() const { return flags_ & DM_BUFFER_FULL_FLAG; }
-        bool IsInactiveTablePresent() const { return flags_ & DM_INACTIVE_PRESENT_FLAG; }
-        bool IsReadOnly() const { return flags_ & DM_READONLY_FLAG; }
-        bool IsSuspended() const { return flags_ & DM_SUSPEND_FLAG; }
-    };
-
     // Removes a device mapper device with the given name.
     // Returns 'true' on success, false otherwise.
     bool DeleteDevice(const std::string& name);
-    bool DeleteDeviceIfExists(const std::string& name);
 
-    // Fetches and returns the complete state of the underlying device mapper
-    // device with given name.
-    std::optional<Info> GetDetailedInfo(const std::string& name) const;
+    // 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;
 
-    // Puts the given device to the specified status, which must be either:
-    // - SUSPENDED: suspend the device, or
-    // - ACTIVE: resumes the device.
-    bool ChangeState(const std::string& name, DmDeviceState state);
-
     // 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.
-    // After creation, |path| contains the result of calling
-    // GetDmDevicePathByName, and the path is guaranteed to exist. If after
-    // |timeout_ms| the path is not available, the device will be deleted and
-    // this function will return false.
-    //
-    // This variant must be used when depending on the device path. The
-    // following manual sequence should not be used:
-    //
-    //   1. CreateDevice(name, table)
-    //   2. GetDmDevicePathByName(name, &path)
-    //   3. fs_mgr::WaitForFile(path, <timeout>)
-    //
-    // This sequence has a race condition where, if another process deletes a
-    // device, CreateDevice may acquire the same path. When this happens, the
-    // WaitForFile() may early-return since ueventd has not yet processed all
-    // of the outstanding udev events. The caller may unexpectedly get an
-    // ENOENT on a system call using the affected path.
-    //
-    // If |timeout_ms| is 0ms, then this function will return true whether or
-    // not |path| is available. It is the caller's responsibility to ensure
-    // there are no races.
-    bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
-                      const std::chrono::milliseconds& timeout_ms);
-
-    // Create a device and activate the given table, without waiting to acquire
-    // a valid path. If the caller will use GetDmDevicePathByName(), it should
-    // use the timeout variant above.
     bool CreateDevice(const std::string& name, const DmTable& table);
 
     // Loads the device mapper table from parameter into the underlying device
@@ -147,10 +96,6 @@
     // 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
@@ -160,28 +105,8 @@
     // 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.
-    //
-    // This returns a path in the format "/dev/block/dm-N" that can be easily
-    // re-used with sysfs.
-    //
-    // WaitForFile() should not be used in conjunction with this call, since it
-    // could race with ueventd.
     bool GetDmDevicePathByName(const std::string& name, std::string* path);
 
-    // Returns a device's unique path as generated by ueventd. This will return
-    // true as long as the device has been created, even if ueventd has not
-    // processed it yet.
-    //
-    // The formatting of this path is /dev/block/mapper/by-uuid/<uuid>.
-    bool GetDeviceUniquePath(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();
 
@@ -198,7 +123,6 @@
     struct TargetInfo {
         struct dm_target_spec spec;
         std::string data;
-        TargetInfo() {}
         TargetInfo(const struct dm_target_spec& spec, const std::string& data)
             : spec(spec), data(data) {}
     };
@@ -208,8 +132,6 @@
     // 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
@@ -222,12 +144,18 @@
     // limit we are imposing here of 256.
     static constexpr uint32_t kMaxPossibleDmDevices = 256;
 
-    bool CreateDevice(const std::string& name, const std::string& uuid = {});
     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;
diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
index ee66653..5c639be 100644
--- a/fs_mgr/libdm/include/libdm/dm_table.h
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -43,20 +43,12 @@
     // 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.
+    // Returns the toatl number of targets.
     size_t num_targets() const { return targets_.size(); }
 
     // Returns the total size represented by the table in terms of number of 512-byte sectors.
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index a78bc71..175b0f0 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -40,18 +40,6 @@
         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_;
@@ -182,125 +170,6 @@
     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 double MergePercent(const Status& status, uint64_t sectors_initial = 0);
-    static bool ParseStatusText(const std::string& text, Status* status);
-    static bool ReportsOverflow(const std::string& target_type);
-    static bool GetDevicesFromParams(const std::string& params, std::string* base_device,
-                                     std::string* cow_device);
-
-  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,
-                       bool set_dun = false)
-        : DmTarget(start, length),
-          cipher_(cipher),
-          key_(key),
-          blockdev_(blockdev),
-          start_sector_(start_sector),
-          set_dun_(set_dun) {}
-
-    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_;
-    bool set_dun_;
-};
-
 }  // namespace dm
 }  // namespace android
 
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
index ad53c11..e6e83f4 100644
--- a/fs_mgr/libdm/include/libdm/loop_control.h
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -17,7 +17,6 @@
 #ifndef _LIBDM_LOOP_CONTROL_H_
 #define _LIBDM_LOOP_CONTROL_H_
 
-#include <chrono>
 #include <string>
 
 #include <android-base/unique_fd.h>
@@ -30,22 +29,12 @@
     LoopControl();
 
     // Attaches the file specified by 'file_fd' to the loop device specified
-    // by 'loopdev'. It is possible that in between allocating and attaching
-    // a loop device, another process attaches to the chosen loop device. If
-    // this happens, Attach() will retry for up to |timeout_ms|. The timeout
-    // should not be zero.
-    //
-    // The caller does not have to call WaitForFile(); it is implicitly called.
-    // The given |timeout_ms| covers both potential sources of timeout.
-    bool Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,
-                std::string* loopdev) const;
+    // 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;
@@ -64,14 +53,13 @@
   public:
     // Create a loop device for the given file descriptor. It is closed when
     // LoopDevice is destroyed only if auto_close is true.
-    LoopDevice(android::base::borrowed_fd fd, const std::chrono::milliseconds& timeout_ms,
-               bool auto_close = false);
+    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.
-    LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms);
+    explicit LoopDevice(const std::string& path);
     ~LoopDevice();
 
-    bool valid() const { return valid_; }
+    bool valid() const { return fd_ != -1 && !device_.empty(); }
     const std::string& device() const { return device_; }
 
     LoopDevice(const LoopDevice&) = delete;
@@ -80,13 +68,12 @@
     LoopDevice(LoopDevice&&) = default;
 
   private:
-    void Init(const std::chrono::milliseconds& timeout_ms);
+    void Init();
 
-    android::base::borrowed_fd fd_;
-    android::base::unique_fd owned_fd_;
+    android::base::unique_fd fd_;
+    bool owns_fd_;
     std::string device_;
     LoopControl control_;
-    bool valid_ = false;
 };
 
 }  // namespace dm
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
index 2e40a18..0beb1a6 100644
--- a/fs_mgr/libdm/loop_control.cpp
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -27,8 +27,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 
-#include "utility.h"
-
 namespace android {
 namespace dm {
 
@@ -39,40 +37,21 @@
     }
 }
 
-bool LoopControl::Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,
-                         std::string* loopdev) const {
-    auto start_time = std::chrono::steady_clock::now();
-    auto condition = [&]() -> WaitResult {
-        if (!FindFreeLoopDevice(loopdev)) {
-            LOG(ERROR) << "Failed to attach, no free loop devices";
-            return WaitResult::Fail;
-        }
+bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
+    if (!FindFreeLoopDevice(loopdev)) {
+        LOG(ERROR) << "Failed to attach, no free loop devices";
+        return false;
+    }
 
-        auto now = std::chrono::steady_clock::now();
-        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
-        if (!WaitForFile(*loopdev, timeout_ms - time_elapsed)) {
-            LOG(ERROR) << "Timed out waiting for path: " << *loopdev;
-            return WaitResult::Fail;
-        }
+    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;
+    }
 
-        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 WaitResult::Fail;
-        }
-
-        if (int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd); rc == 0) {
-            return WaitResult::Done;
-        }
-        if (errno != EBUSY) {
-            PLOG(ERROR) << "Failed LOOP_SET_FD";
-            return WaitResult::Fail;
-        }
-        return WaitResult::Wait;
-    };
-    if (!WaitForCondition(condition, timeout_ms)) {
-        LOG(ERROR) << "Timed out trying to acquire a loop device";
+    int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
+    if (rc < 0) {
+        PLOG(ERROR) << "Failed LOOP_SET_FD";
         return false;
     }
     return true;
@@ -112,55 +91,30 @@
     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(android::base::borrowed_fd fd, const std::chrono::milliseconds& timeout_ms,
-                       bool auto_close)
-    : fd_(fd), owned_fd_(-1) {
-    if (auto_close) {
-        owned_fd_.reset(fd.get());
-    }
-    Init(timeout_ms);
-}
-
-LoopDevice::LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms)
-    : fd_(-1), owned_fd_(-1) {
-    owned_fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
-    if (owned_fd_ == -1) {
+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;
     }
-    fd_ = owned_fd_;
-    Init(timeout_ms);
+    Init();
 }
 
 LoopDevice::~LoopDevice() {
     if (valid()) {
         control_.Detach(device_);
     }
+    if (!owns_fd_) {
+        (void)fd_.release();
+    }
 }
 
-void LoopDevice::Init(const std::chrono::milliseconds& timeout_ms) {
-    valid_ = control_.Attach(fd_.get(), timeout_ms, &device_);
+void LoopDevice::Init() {
+    control_.Attach(fd_, &device_);
 }
 
 }  // namespace dm
diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp
index 0749f26..08bdc00 100644
--- a/fs_mgr/libdm/loop_control_test.cpp
+++ b/fs_mgr/libdm/loop_control_test.cpp
@@ -53,7 +53,7 @@
     unique_fd fd = TempFile();
     ASSERT_GE(fd, 0);
 
-    LoopDevice loop(fd, 10s);
+    LoopDevice loop(fd);
     ASSERT_TRUE(loop.valid());
 
     char buffer[6];
diff --git a/fs_mgr/libdm/test_util.h b/fs_mgr/libdm/test_util.h
index 6671364..96b051c 100644
--- a/fs_mgr/libdm/test_util.h
+++ b/fs_mgr/libdm/test_util.h
@@ -20,12 +20,8 @@
 #include <android-base/unique_fd.h>
 #include <stddef.h>
 
-#include <chrono>
 #include <string>
 
-#include <libdm/dm.h>
-#include <libdm/dm_table.h>
-
 namespace android {
 namespace dm {
 
@@ -33,50 +29,6 @@
 // created with a fixed size.
 android::base::unique_fd CreateTempFile(const std::string& name, size_t size);
 
-// 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, &path_, std::chrono::seconds(5));
-    }
-    TempDevice(TempDevice&& other) noexcept
-        : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
-        other.valid_ = false;
-    }
-    ~TempDevice() {
-        if (valid_) {
-            dm_.DeleteDevice(name_);
-        }
-    }
-    bool Destroy() {
-        if (!valid_) {
-            return false;
-        }
-        valid_ = false;
-        return dm_.DeleteDevice(name_);
-    }
-    std::string path() const { return 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_;
-    std::string path_;
-    bool valid_;
-};
-
 }  // namespace dm
 }  // namespace android
 
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
deleted file mode 100644
index eccf2fb..0000000
--- a/fs_mgr/libdm/utility.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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 <errno.h>
-#include <unistd.h>
-
-#include <thread>
-
-using namespace std::literals;
-
-namespace android {
-namespace dm {
-
-bool WaitForCondition(const std::function<WaitResult()>& condition,
-                      const std::chrono::milliseconds& timeout_ms) {
-    auto start_time = std::chrono::steady_clock::now();
-    while (true) {
-        auto result = condition();
-        if (result == WaitResult::Done) return true;
-        if (result == WaitResult::Fail) return false;
-
-        std::this_thread::sleep_for(20ms);
-
-        auto now = std::chrono::steady_clock::now();
-        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
-        if (time_elapsed > timeout_ms) return false;
-    }
-}
-
-bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
-    auto condition = [&]() -> WaitResult {
-        // If the file exists but returns EPERM or something, we consider the
-        // condition met.
-        if (access(path.c_str(), F_OK) != 0) {
-            if (errno == ENOENT) return WaitResult::Wait;
-        }
-        return WaitResult::Done;
-    };
-    return WaitForCondition(condition, timeout_ms);
-}
-
-}  // namespace dm
-}  // namespace android
diff --git a/fs_mgr/libdm/utility.h b/fs_mgr/libdm/utility.h
deleted file mode 100644
index f1dce9e..0000000
--- a/fs_mgr/libdm/utility.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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 <functional>
-
-namespace android {
-namespace dm {
-
-enum class WaitResult { Wait, Done, Fail };
-
-bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms);
-bool WaitForCondition(const std::function<WaitResult()>& condition,
-                      const std::chrono::milliseconds& timeout_ms);
-
-}  // namespace dm
-}  // namespace android
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..32fc3d2
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/Android.bp
@@ -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.
+//
+
+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: [
+        "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..e3803d5
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -0,0 +1,682 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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>
+
+namespace android {
+namespace fiemap_writer {
+
+// 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 DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
+    // TODO: Stop popping the device mapper stack if dm-linear target is found
+    if (!::android::base::StartsWith(bdev, "dm-")) {
+        // We are at the bottom of the device mapper stack.
+        *bdev_raw = bdev;
+        return true;
+    }
+
+    std::string dm_leaf_dir = ::android::base::StringPrintf("/sys/block/%s/slaves", bdev.c_str());
+    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..9486122
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.
+    // If an intermediate device-mapper layer would not maintain a 1:1 mapping (i.e. is a non-
+    // trivial dm-linear), then this will fail. If device-mapper nodes are 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..a0ccc10
--- /dev/null
+++ b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
@@ -0,0 +1,297 @@
+/*
+ * 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();
+        remaining_bytes -= writer->size();
+
+        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/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data b/fs_mgr/libfiemap_writer/testdata/file_4k
similarity index 76%
copy from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
copy to fs_mgr/libfiemap_writer/testdata/file_4k
index 1674733..08e7df1 100644
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
+++ b/fs_mgr/libfiemap_writer/testdata/file_4k
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data b/fs_mgr/libfiemap_writer/testdata/unaligned_file
similarity index 76%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
rename to fs_mgr/libfiemap_writer/testdata/unaligned_file
index 1674733..c107c26 100644
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
+++ 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/libvbmeta/include/libvbmeta/libvbmeta.h b/fs_mgr/libfiemap_writer/utility.h
similarity index 69%
rename from fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
rename to fs_mgr/libfiemap_writer/utility.h
index ab7ba73..2d418da 100644
--- a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
+++ b/fs_mgr/libfiemap_writer/utility.h
@@ -16,14 +16,15 @@
 
 #pragma once
 
-#include <map>
 #include <string>
 
 namespace android {
-namespace fs_mgr {
+namespace fiemap_writer {
 
-bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
-                            const std::map<std::string, std::string>& images_path);
+// 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 fs_mgr
-}  // namespace android
\ No newline at end of file
+}  // namespace fiemap_writer
+}  // namespace android
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 414a186..a3c76ab 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -61,7 +61,6 @@
         "libavb",
         "libavb_host_sysdeps",
         "libdm",
-        "libext2_uuid",
         "libfs_avb",
         "libfstab",
         "libgtest_host",
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 4505382..d9650f3 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -104,23 +104,31 @@
     }
     table.set_readonly(true);
 
-    std::chrono::milliseconds timeout = {};
-    if (wait_for_verity_dev) timeout = 1s;
-
-    std::string dev_path;
     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, &dev_path, timeout)) {
+    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;
 }
 
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 784eb9c..0d342d3 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -101,10 +101,10 @@
 TEST_F(AvbUtilTest, DeriveAvbPartitionName) {
     // The fstab_entry to test.
     FstabEntry fstab_entry = {
-            .blk_device = "/dev/block/dm-1",  // a dm-linear device (logical)
-            .logical_partition_name = "system",
-            .mount_point = "/system",
-            .fs_type = "ext4",
+        .blk_device = "/dev/block/dm-1",  // a dm-linear device (logical)
+        .mount_point = "/system",
+        .fs_type = "ext4",
+        .logical_partition_name = "system",
     };
 
     // Logical partitions.
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index ea0fca8..b504161 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -17,6 +17,7 @@
 liblp_lib_deps = [
     "libbase",
     "liblog",
+    "libcrypto",
     "libcrypto_utils",
     "libsparse",
     "libext4_utils",
@@ -35,14 +36,11 @@
         "builder.cpp",
         "images.cpp",
         "partition_opener.cpp",
-        "property_fetcher.cpp",
         "reader.cpp",
         "utility.cpp",
         "writer.cpp",
     ],
-    shared_libs: [
-        "libcrypto",
-    ] + liblp_lib_deps,
+    shared_libs: liblp_lib_deps,
     target: {
         windows: {
             enabled: true,
@@ -56,55 +54,29 @@
     export_include_dirs: ["include"],
 }
 
-filegroup {
-    name: "liblp_test_srcs",
-    srcs: [
-        "builder_test.cpp",
-        "device_test.cpp",
-        "io_test.cpp",
-        "test_partition_opener.cpp",
-        "utility_test.cpp",
-    ],
-}
-
-cc_defaults {
-    name: "liblp_test_defaults",
+cc_test {
+    name: "liblp_test_static",
     defaults: ["fs_mgr_defaults"],
     cppflags: [
         "-Wno-unused-parameter",
     ],
     static_libs: [
-        "libcutils",
         "libgmock",
         "libfs_mgr",
         "liblp",
-        "libcrypto_static",
     ] + liblp_lib_deps,
-    header_libs: [
-        "libstorage_literals_headers",
-    ],
     stl: "libc++_static",
-    srcs: [":liblp_test_srcs"],
+    srcs: [
+        "builder_test.cpp",
+        "io_test.cpp",
+        "test_partition_opener.cpp",
+        "utility_test.cpp",
+    ],
+    target: {
+        android: {
+            static_libs: [
+                "libcutils",
+            ],
+        },
+    },
 }
-
-cc_test {
-    name: "liblp_test",
-    defaults: ["liblp_test_defaults"],
-    test_config: "liblp_test.xml",
-    test_suites: ["device-tests"],
-}
-
-cc_test {
-    name: "vts_core_liblp_test",
-    defaults: ["liblp_test_defaults"],
-    test_suites: ["vts-core"],
-    auto_gen_config: true,
-    test_min_api_level: 29,
-    require_root: true,
-}
-
-cc_test {
-    name: "vts_kernel_liblp_test",
-    defaults: ["liblp_test_defaults"],
-}
-
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
index 2eb0ad1..fe1002c 100644
--- a/fs_mgr/liblp/AndroidTest.xml
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -21,8 +21,8 @@
     </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/vts_kernel_liblp_test/vts_kernel_liblp_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_kernel_liblp_test/vts_kernel_liblp_test" />
+        <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" />
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
deleted file mode 100644
index 04bcbda..0000000
--- a/fs_mgr/liblp/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "liblp_test"
-    }
-  ]
-}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 54350a5..27222af 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -20,16 +20,21 @@
 
 #include <algorithm>
 
+#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
 
 #include "liblp/liblp.h"
-#include "liblp/property_fetcher.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.";
@@ -40,16 +45,12 @@
     return true;
 }
 
-Interval LinearExtent::AsInterval() const {
-    return Interval(device_index(), physical_sector(), end_sector());
-}
-
 bool ZeroExtent::AddTo(LpMetadata* out) const {
     out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
     return true;
 }
 
-Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
+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) {
@@ -100,20 +101,6 @@
     DCHECK(size_ == aligned_size);
 }
 
-Partition Partition::GetBeginningExtents(uint64_t aligned_size) const {
-    Partition p(name_, group_name_, attributes_);
-    for (const auto& extent : extents_) {
-        auto le = extent->AsLinearExtent();
-        if (le) {
-            p.AddExtent(std::make_unique<LinearExtent>(*le));
-        } else {
-            p.AddExtent(std::make_unique<ZeroExtent>(extent->num_sectors()));
-        }
-    }
-    p.ShrinkTo(aligned_size);
-    return p;
-}
-
 uint64_t Partition::BytesOnDisk() const {
     uint64_t sectors = 0;
     for (const auto& extent : extents_) {
@@ -158,7 +145,7 @@
     }
     if (opener) {
         for (size_t i = 0; i < builder->block_devices_.size(); i++) {
-            std::string partition_name = builder->GetBlockDevicePartitionName(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);
@@ -171,42 +158,21 @@
 std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,
                                                                const std::string& source_partition,
                                                                uint32_t source_slot_number,
-                                                               uint32_t target_slot_number,
-                                                               bool always_keep_source_slot) {
+                                                               uint32_t target_slot_number) {
     auto metadata = ReadMetadata(opener, source_partition, source_slot_number);
     if (!metadata) {
         return nullptr;
     }
 
-    // On retrofit DAP devices, modify the metadata so that it is suitable for being written
-    // to the target slot later. We detect retrofit DAP devices by checking the super partition
-    // name and system properties.
-    // See comments for UpdateMetadataForOtherSuper.
+    // 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 (android::fs_mgr::GetBlockDevicePartitionName(*super_device) != "super" &&
-        IsRetrofitDynamicPartitionsDevice()) {
-        if (!UpdateMetadataForOtherSuper(metadata.get(), source_slot_number, target_slot_number)) {
-            return nullptr;
-        }
+    if (GetBlockDevicePartitionName(*super_device) == "super") {
+        return New(*metadata.get(), &opener);
     }
 
-    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false) &&
-        !always_keep_source_slot) {
-        if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
-                                              target_slot_number)) {
-            return nullptr;
-        }
-    }
-
-    return New(*metadata.get(), &opener);
-}
-
-// For retrofit DAP devices, there are (conceptually) two super partitions. We'll need to translate
-// block device and group names to update their slot suffixes.
-// (On the other hand, On non-retrofit DAP devices there is only one location for metadata: the
-// super partition. update_engine will remove and resize partitions as needed.)
-bool MetadataBuilder::UpdateMetadataForOtherSuper(LpMetadata* metadata, uint32_t source_slot_number,
-                                                  uint32_t target_slot_number) {
     // 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();
@@ -219,15 +185,14 @@
     // 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 =
-                android::fs_mgr::GetBlockDevicePartitionName(source_block_device);
+        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 false;
+            return nullptr;
         }
         std::string new_name =
                 partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
@@ -236,15 +201,20 @@
         auto new_device = source_block_device;
         if (!UpdateBlockDevicePartitionName(&new_device, new_name)) {
             LERROR << "Partition name too long: " << new_name;
-            return false;
+            return nullptr;
         }
         metadata->block_devices.emplace_back(new_device);
     }
 
-    return true;
+    return New(*metadata.get(), &opener);
 }
 
-MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
+void MetadataBuilder::OverrideABForTesting(bool ab_device) {
+    sABOverrideSet = true;
+    sABOverrideValue = ab_device;
+}
+
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false), ignore_slot_suffixing_(false) {
     memset(&geometry_, 0, sizeof(geometry_));
     geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
     geometry_.struct_size = sizeof(geometry_);
@@ -252,7 +222,7 @@
     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_MIN;
+    header_.minor_version = LP_METADATA_MINOR_VERSION;
     header_.header_size = sizeof(header_);
     header_.partitions.entry_size = sizeof(LpMetadataPartition);
     header_.extents.entry_size = sizeof(LpMetadataExtent);
@@ -403,7 +373,7 @@
             block_devices_.emplace_back(out);
         }
     }
-    if (GetBlockDevicePartitionName(0) != super_partition) {
+    if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
         LERROR << "No super partition was specified.";
         return false;
     }
@@ -440,13 +410,13 @@
     geometry_.metadata_slot_count = metadata_slot_count;
     geometry_.logical_block_size = logical_block_size;
 
-    if (!AddGroup(std::string(kDefaultGroup), 0)) {
+    if (!AddGroup(kDefaultGroup, 0)) {
         return false;
     }
     return true;
 }
 
-bool MetadataBuilder::AddGroup(std::string_view group_name, uint64_t maximum_size) {
+bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
     if (FindGroup(group_name)) {
         LERROR << "Group already exists: " << group_name;
         return false;
@@ -459,7 +429,7 @@
     return AddPartition(name, kDefaultGroup, attributes);
 }
 
-Partition* MetadataBuilder::AddPartition(std::string_view name, std::string_view group_name,
+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.";
@@ -473,11 +443,16 @@
         LERROR << "Could not find partition group: " << group_name;
         return nullptr;
     }
+    if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" && !ignore_slot_suffixing_ &&
+        GetPartitionSlotSuffix(name).empty()) {
+        LERROR << "Unsuffixed partition not allowed on A/B device: " << name;
+        return nullptr;
+    }
     partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
     return partitions_.back().get();
 }
 
-Partition* MetadataBuilder::FindPartition(std::string_view name) {
+Partition* MetadataBuilder::FindPartition(const std::string& name) {
     for (const auto& partition : partitions_) {
         if (partition->name() == name) {
             return partition.get();
@@ -486,7 +461,7 @@
     return nullptr;
 }
 
-PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) {
+PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) {
     for (const auto& group : groups_) {
         if (group->name() == group_name) {
             return group.get();
@@ -506,7 +481,7 @@
     return total;
 }
 
-void MetadataBuilder::RemovePartition(std::string_view name) {
+void MetadataBuilder::RemovePartition(const std::string& name) {
     for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
         if ((*iter)->name() == name) {
             partitions_.erase(iter);
@@ -599,48 +574,18 @@
     return true;
 }
 
-Interval Interval::Intersect(const Interval& a, const Interval& b) {
-    Interval ret = a;
-    if (a.device_index != b.device_index) {
-        ret.start = ret.end = a.start;  // set length to 0 to indicate no intersection.
-        return ret;
-    }
-    ret.start = std::max(a.start, b.start);
-    ret.end = std::max(ret.start, std::min(a.end, b.end));
-    return ret;
-}
-
-std::vector<Interval> Interval::Intersect(const std::vector<Interval>& a,
-                                          const std::vector<Interval>& b) {
-    std::vector<Interval> ret;
-    for (const Interval& a_interval : a) {
-        for (const Interval& b_interval : b) {
-            auto intersect = Intersect(a_interval, b_interval);
-            if (intersect.length() > 0) ret.emplace_back(std::move(intersect));
-        }
-    }
-    return ret;
-}
-
-std::unique_ptr<Extent> Interval::AsExtent() const {
-    return std::make_unique<LinearExtent>(length(), device_index, start);
-}
-
-bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size,
-                                    const std::vector<Interval>& free_region_hint) {
+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();
-    if (!free_region_hint.empty())
-        free_regions = Interval::Intersect(free_regions, free_region_hint);
 
     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() && ShouldHalveSuper() && GetPartitionSlotSuffix(partition->name()) == "_b") {
+    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);
@@ -700,7 +645,7 @@
     return true;
 }
 
-std::vector<Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
+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;
@@ -846,11 +791,6 @@
             return nullptr;
         }
 
-        if (partition->attributes() & LP_PARTITION_ATTR_UPDATED) {
-            static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;
-            metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);
-        }
-
         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());
@@ -911,7 +851,7 @@
 bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
                                             uint32_t* index) const {
     for (size_t i = 0; i < block_devices_.size(); i++) {
-        if (GetBlockDevicePartitionName(i) == partition_name) {
+        if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
             *index = i;
             return true;
         }
@@ -976,8 +916,7 @@
     return true;
 }
 
-bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
-                                      const std::vector<Interval>& free_region_hint) {
+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();
@@ -987,7 +926,7 @@
     }
 
     if (aligned_size > old_size) {
-        if (!GrowPartition(partition, aligned_size, free_region_hint)) {
+        if (!GrowPartition(partition, aligned_size)) {
             return false;
         }
     } else if (aligned_size < partition->size()) {
@@ -1009,7 +948,7 @@
     return names;
 }
 
-void MetadataBuilder::RemoveGroupAndPartitions(std::string_view group_name) {
+void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
     if (group_name == kDefaultGroup) {
         // Cannot remove the default group.
         return;
@@ -1037,8 +976,7 @@
     // 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 &&
-           android::fs_mgr::GetBlockDevicePartitionName(first) ==
-                   android::fs_mgr::GetBlockDevicePartitionName(second);
+           GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
 }
 
 bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
@@ -1111,18 +1049,19 @@
     auto_slot_suffixing_ = true;
 }
 
-bool MetadataBuilder::IsABDevice() {
-    return !IPropertyFetcher::GetInstance()->GetProperty("ro.boot.slot_suffix", "").empty();
+void MetadataBuilder::IgnoreSlotSuffixing() {
+    ignore_slot_suffixing_ = true;
 }
 
-bool MetadataBuilder::IsRetrofitDynamicPartitionsDevice() {
-    return IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions_retrofit",
-                                                            false);
+bool MetadataBuilder::IsABDevice() const {
+    if (sABOverrideSet) {
+        return sABOverrideValue;
+    }
+    return android::base::GetBoolProperty("ro.build.ab_update", false);
 }
 
-bool MetadataBuilder::ShouldHalveSuper() const {
-    return GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
-           !IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false);
+bool MetadataBuilder::IsRetrofitDevice() const {
+    return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
 }
 
 bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -1138,7 +1077,7 @@
     return true;
 }
 
-std::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(std::string_view group_name) {
+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) {
@@ -1148,7 +1087,7 @@
     return partitions;
 }
 
-bool MetadataBuilder::ChangePartitionGroup(Partition* partition, std::string_view group_name) {
+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;
@@ -1186,15 +1125,5 @@
     return true;
 }
 
-std::string MetadataBuilder::GetBlockDevicePartitionName(uint64_t index) const {
-    return index < block_devices_.size()
-                   ? android::fs_mgr::GetBlockDevicePartitionName(block_devices_[index])
-                   : "";
-}
-
-uint64_t MetadataBuilder::logical_block_size() const {
-    return geometry_.logical_block_size;
-}
-
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index a67ffa7..46bfe92 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,35 +14,34 @@
  * limitations under the License.
  */
 
+#include <fs_mgr.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
-#include <storage_literals/storage_literals.h>
 
-#include "liblp_test.h"
 #include "utility.h"
 
 using namespace std;
-using namespace android::storage_literals;
 using namespace android::fs_mgr;
-using namespace android::fs_mgr::testing;
-using ::testing::_;
-using ::testing::AnyNumber;
 using ::testing::ElementsAre;
-using ::testing::NiceMock;
-using ::testing::Return;
 
 class Environment : public ::testing::Environment {
   public:
-    void SetUp() override { ResetMockPropertyFetcher(); }
+    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 LiblpTest {};
+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);
@@ -107,13 +106,6 @@
     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);
@@ -351,7 +343,7 @@
     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_MIN);
+    EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
 
     ASSERT_EQ(exported->partitions.size(), 2);
     ASSERT_EQ(exported->extents.size(), 3);
@@ -449,6 +441,23 @@
     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);
@@ -593,6 +602,13 @@
     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);
@@ -749,14 +765,21 @@
     EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {"system"}));
 }
 
+TEST_F(BuilderTest, UnsuffixedPartitions) {
+    MetadataBuilder::OverrideABForTesting(true);
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_EQ(builder->AddPartition("system", 0), nullptr);
+    ASSERT_NE(builder->AddPartition("system_a", 0), nullptr);
+}
+
 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)
-    ON_CALL(*GetMockedPropertyFetcher(), GetProperty("ro.boot.slot_suffix", _))
-            .WillByDefault(Return("_a"));
-
+    MetadataBuilder::OverrideABForTesting(true);
     auto builder = MetadataBuilder::New(device_info, 65536, 2);
     ASSERT_NE(builder, nullptr);
     Partition* system_a = builder->AddPartition("system_a", 0);
@@ -882,38 +905,3 @@
     std::set<std::string> partitions_to_keep{"system_a", "vendor_a", "product_a"};
     ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));
 }
-
-// Interval has operator< defined; it is not appropriate to re-define Interval::operator== that
-// compares device index.
-namespace android {
-namespace fs_mgr {
-bool operator==(const Interval& a, const Interval& b) {
-    return a.device_index == b.device_index && a.start == b.start && a.end == b.end;
-}
-}  // namespace fs_mgr
-}  // namespace android
-
-TEST_F(BuilderTest, Interval) {
-    EXPECT_EQ(0u, Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 100)).length());
-    EXPECT_EQ(Interval(0, 100, 150),
-              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 150)));
-    EXPECT_EQ(Interval(0, 100, 200),
-              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 200)));
-    EXPECT_EQ(Interval(0, 100, 200),
-              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 250)));
-    EXPECT_EQ(Interval(0, 100, 200),
-              Interval::Intersect(Interval(0, 100, 200), Interval(0, 100, 200)));
-    EXPECT_EQ(Interval(0, 150, 200),
-              Interval::Intersect(Interval(0, 100, 200), Interval(0, 150, 250)));
-    EXPECT_EQ(0u, Interval::Intersect(Interval(0, 100, 200), Interval(0, 200, 250)).length());
-
-    auto v = Interval::Intersect(std::vector<Interval>{Interval(0, 0, 50), Interval(0, 100, 150)},
-                                 std::vector<Interval>{Interval(0, 25, 125)});
-    ASSERT_EQ(2, v.size());
-    EXPECT_EQ(Interval(0, 25, 50), v[0]);
-    EXPECT_EQ(Interval(0, 100, 125), v[1]);
-
-    EXPECT_EQ(0u, Interval::Intersect(std::vector<Interval>{Interval(0, 0, 50)},
-                                      std::vector<Interval>{Interval(0, 100, 150)})
-                          .size());
-}
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
deleted file mode 100644
index 382d53d..0000000
--- a/fs_mgr/liblp/device_test.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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_mgr.h>
-#include <fstab/fstab.h>
-#include <gtest/gtest.h>
-#include <liblp/liblp.h>
-#include <liblp/metadata_format.h>
-#include <liblp/partition_opener.h>
-#include <liblp/property_fetcher.h>
-
-#include "liblp_test.h"
-
-using namespace android::fs_mgr;
-using namespace android::fs_mgr::testing;
-using ::testing::Return;
-
-// Compliance test on the actual device with dynamic partitions.
-class DeviceTest : public LiblpTest {
-  public:
-    void SetUp() override {
-        // Read real properties.
-        IPropertyFetcher::OverrideForTesting(std::make_unique<PropertyFetcher>());
-        if (!IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions",
-                                                              false)) {
-            GTEST_SKIP() << "Device doesn't have dynamic partitions enabled, skipping";
-        }
-    }
-};
-
-TEST_F(DeviceTest, BlockDeviceInfo) {
-    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.
-    EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
-    EXPECT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
-    EXPECT_LE(device_info.alignment_offset, INT_MAX);
-    EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
-
-    // Having an alignment offset > alignment doesn't really make sense.
-    EXPECT_LT(device_info.alignment_offset, device_info.alignment);
-}
-
-TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
-    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);
-    EXPECT_NE(metadata, nullptr);
-}
-
-TEST_F(DeviceTest, ReadSuperPartitionOtherSlot) {
-    auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
-    if (other_slot_suffix.empty()) {
-        GTEST_SKIP() << "No other slot, skipping";
-    }
-    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions_retrofit",
-                                                         false)) {
-        GTEST_SKIP() << "Device with retrofit dynamic partition may not have metadata at other "
-                     << "slot, skipping";
-    }
-
-    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);
-    EXPECT_NE(other_metadata, nullptr);
-}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 6b842b3..db27022 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -17,7 +17,6 @@
 #include "images.h"
 
 #include <limits.h>
-#include <sys/stat.h>
 
 #include <android-base/file.h>
 
@@ -28,45 +27,12 @@
 namespace android {
 namespace fs_mgr {
 
-using android::base::borrowed_fd;
 using android::base::unique_fd;
 
 #if defined(_WIN32)
 static const int O_NOFOLLOW = 0;
 #endif
 
-static bool IsEmptySuperImage(borrowed_fd fd) {
-    struct stat s;
-    if (fstat(fd.get(), &s) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " fstat failed";
-        return false;
-    }
-    if (s.st_size < LP_METADATA_GEOMETRY_SIZE) {
-        return false;
-    }
-
-    // Rewind back to the start, read the geometry struct.
-    LpMetadataGeometry geometry = {};
-    if (SeekFile64(fd.get(), 0, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
-        return false;
-    }
-    if (!android::base::ReadFully(fd, &geometry, sizeof(geometry))) {
-        PERROR << __PRETTY_FUNCTION__ << " read failed";
-        return false;
-    }
-    return geometry.magic == LP_METADATA_GEOMETRY_MAGIC;
-}
-
-bool IsEmptySuperImage(const std::string& file) {
-    unique_fd fd = GetControlFileOrOpen(file, O_RDONLY | O_CLOEXEC);
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " open failed";
-        return false;
-    }
-    return IsEmptySuperImage(fd);
-}
-
 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) {
@@ -123,8 +89,8 @@
     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));
+bool WriteToImageFile(const char* file, const LpMetadata& input) {
+    unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
         return false;
@@ -183,8 +149,8 @@
     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));
+bool ImageBuilder::Export(const char* file) {
+    unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
     if (fd < 0) {
         PERROR << "open failed: " << file;
         return false;
@@ -472,7 +438,7 @@
     return temp_fds_.back().get();
 }
 
-bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
+bool WriteToImageFile(const char* 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);
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index a284d2e..75060f9 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -41,7 +41,7 @@
                  const std::map<std::string, std::string>& images, bool sparsify);
 
     bool Build();
-    bool Export(const std::string& file);
+    bool Export(const char* file);
     bool ExportFiles(const std::string& dir);
     bool IsValid() const;
 
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 1e9d636..c706f2a 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -24,7 +24,6 @@
 #include <memory>
 #include <optional>
 #include <set>
-#include <string_view>
 
 #include "liblp.h"
 #include "partition_opener.h"
@@ -33,14 +32,10 @@
 namespace fs_mgr {
 
 class LinearExtent;
-struct Interval;
 
 // By default, partitions are aligned on a 1MiB boundary.
-static constexpr uint32_t kDefaultPartitionAlignment = 1024 * 1024;
-static constexpr uint32_t kDefaultBlockSize = 4096;
-
-// Name of the default group in a metadata.
-static constexpr std::string_view kDefaultGroup = "default";
+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 {
@@ -75,8 +70,6 @@
         return sector >= physical_sector_ && sector < end_sector();
     }
 
-    Interval AsInterval() const;
-
   private:
     uint32_t device_index_;
     uint64_t physical_sector_;
@@ -94,7 +87,7 @@
     friend class MetadataBuilder;
 
   public:
-    explicit PartitionGroup(std::string_view name, uint64_t maximum_size)
+    explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
         : name_(name), maximum_size_(maximum_size) {}
 
     const std::string& name() const { return name_; }
@@ -111,7 +104,7 @@
     friend class MetadataBuilder;
 
   public:
-    Partition(std::string_view name, std::string_view group_name, uint32_t attributes);
+    Partition(const std::string& name, const std::string& group_name, uint32_t attributes);
 
     // Add a raw extent.
     void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -126,19 +119,12 @@
     const std::string& name() const { return name_; }
     const std::string& group_name() const { return group_name_; }
     uint32_t attributes() const { return attributes_; }
-    void set_attributes(uint32_t attributes) { attributes_ = attributes; }
     const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
     uint64_t size() const { return size_; }
 
-    // Return a copy of *this, but with extents that includes only the first
-    // |aligned_size| bytes. |aligned_size| should be aligned to
-    // logical_block_size() of the MetadataBuilder that this partition belongs
-    // to.
-    Partition GetBeginningExtents(uint64_t aligned_size) const;
-
   private:
     void ShrinkTo(uint64_t aligned_size);
-    void set_group_name(std::string_view group_name) { group_name_ = group_name; }
+    void set_group_name(const std::string& group_name) { group_name_ = group_name; }
 
     std::string name_;
     std::string group_name_;
@@ -147,35 +133,6 @@
     uint64_t size_;
 };
 
-// An interval in the metadata. This is similar to a LinearExtent with one difference.
-// LinearExtent represents a "used" region in the metadata, while Interval can also represent
-// an "unused" region.
-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::unique_ptr<Extent> AsExtent() const;
-
-    // Intersect |a| with |b|.
-    // If no intersection, result has 0 length().
-    static Interval Intersect(const Interval& a, const Interval& b);
-
-    // Intersect two lists of intervals, and store result to |a|.
-    static std::vector<Interval> Intersect(const std::vector<Interval>& a,
-                                           const std::vector<Interval>& b);
-};
-
 class MetadataBuilder {
   public:
     // Construct an empty logical partition table builder given the specified
@@ -209,13 +166,10 @@
     // metadata may not have the target slot's devices listed yet, in which
     // case, it is automatically upgraded to include all available block
     // devices.
-    // If |always_keep_source_slot| is set, on a Virtual A/B device, source slot
-    // partitions are kept. This is useful when applying a downgrade package.
     static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
                                                          const std::string& source_partition,
                                                          uint32_t source_slot_number,
-                                                         uint32_t target_slot_number,
-                                                         bool always_keep_source_slot = false);
+                                                         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.
@@ -242,12 +196,15 @@
         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(std::string_view group_name, uint64_t maximum_size);
+    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.
@@ -255,7 +212,7 @@
 
     // 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(std::string_view name, std::string_view group_name,
+    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
@@ -263,13 +220,13 @@
     Partition* AddPartition(const std::string& name, uint32_t attributes);
 
     // Delete a partition by name if it exists.
-    void RemovePartition(std::string_view name);
+    void RemovePartition(const std::string& name);
 
     // Find a partition by name. If no partition is found, nullptr is returned.
-    Partition* FindPartition(std::string_view name);
+    Partition* FindPartition(const std::string& name);
 
     // Find a group by name. If no group is found, nullptr is returned.
-    PartitionGroup* FindGroup(std::string_view name);
+    PartitionGroup* FindGroup(const std::string& name);
 
     // Add a predetermined extent to a partition.
     bool AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -285,20 +242,16 @@
     //
     // Note, this is an in-memory operation, and it does not alter the
     // underlying filesystem or contents of the partition on disk.
-    //
-    // If |free_region_hint| is not empty, it will only try to allocate extents
-    // in regions within the list.
-    bool ResizePartition(Partition* partition, uint64_t requested_size,
-                         const std::vector<Interval>& free_region_hint = {});
+    bool ResizePartition(Partition* partition, uint64_t requested_size);
 
     // Return the list of partitions belonging to a group.
-    std::vector<Partition*> ListPartitionsInGroup(std::string_view group_name);
+    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, std::string_view group_name);
+    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
@@ -314,7 +267,7 @@
     std::vector<std::string> ListGroups() const;
 
     // Remove all partitions belonging to a group, then remove the group.
-    void RemoveGroupAndPartitions(std::string_view group_name);
+    void RemoveGroupAndPartitions(const std::string& group_name);
 
     // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.
     void SetAutoSlotSuffixing();
@@ -333,14 +286,6 @@
     // Return true if a block device is found, else false.
     bool HasBlockDevice(const std::string& partition_name) const;
 
-    // Return the name of the block device at |index|.
-    std::string GetBlockDevicePartitionName(uint64_t index) const;
-
-    // Return the list of free regions not occupied by extents in the metadata.
-    std::vector<Interval> GetFreeRegions() const;
-
-    uint64_t logical_block_size() const;
-
   private:
     MetadataBuilder();
     MetadataBuilder(const MetadataBuilder&) = delete;
@@ -350,8 +295,7 @@
     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,
-                       const std::vector<Interval>& free_region_hint);
+    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;
@@ -362,18 +306,26 @@
     void ImportExtents(Partition* dest, const LpMetadata& metadata,
                        const LpMetadataPartition& source);
     bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
-
-    // Return true if the device is an AB device.
-    static bool IsABDevice();
-
-    // Return true if the device is retrofitting dynamic partitions.
-    static bool IsRetrofitDynamicPartitionsDevice();
-
-    // Return true if _b partitions should be prioritized at the second half of the device.
-    bool ShouldHalveSuper() const;
-
+    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;
@@ -384,8 +336,8 @@
                                                     const std::vector<Interval>& free_list,
                                                     uint64_t sectors_needed) const;
 
-    static bool UpdateMetadataForOtherSuper(LpMetadata* metadata, uint32_t source_slot_number,
-                                            uint32_t target_slot_number);
+    static bool sABOverrideValue;
+    static bool sABOverrideSet;
 
     LpMetadataGeometry geometry_;
     LpMetadataHeader header_;
@@ -393,6 +345,7 @@
     std::vector<std::unique_ptr<PartitionGroup>> groups_;
     std::vector<LpMetadataBlockDevice> block_devices_;
     bool auto_slot_suffixing_;
+    bool ignore_slot_suffixing_;
 };
 
 // Read BlockDeviceInfo for a given block device. This always returns false
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index cd860cd..5f782b0 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -70,18 +70,11 @@
                           uint32_t slot_number);
 std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
 
-// Returns whether an image is an "empty" image or not. An empty image contains
-// only metadata. Unlike a flashed block device, there are no reserved bytes or
-// backup sections, and only one slot is stored (even if multiple slots are
-// supported). It is a format specifically for storing only metadata.
-bool IsEmptySuperImage(const std::string& file);
-
 // Read/Write logical partition metadata to an image file, for diagnostics or
-// flashing. If no partition images are specified, the file will be in the
-// empty format.
-bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
+// flashing.
+bool WriteToImageFile(const char* 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);
+bool WriteToImageFile(const char* 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);
 
@@ -114,10 +107,6 @@
 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
 
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 6e928b4..8934aaf 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -39,11 +39,7 @@
 
 /* Current metadata version. */
 #define LP_METADATA_MAJOR_VERSION 10
-#define LP_METADATA_MINOR_VERSION_MIN 0
-#define LP_METADATA_MINOR_VERSION_MAX 1
-
-/* Metadata version needed to use the UPDATED partition attribute. */
-#define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1
+#define LP_METADATA_MINOR_VERSION 0
 
 /* Attributes for the LpMetadataPartition::attributes field.
  *
@@ -62,20 +58,8 @@
  */
 #define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
 
-/* This flag is applied automatically when using MetadataBuilder::NewForUpdate.
- * It signals that the partition was created (or modified) for a snapshot-based
- * update. If this flag is not present, the partition was likely flashed via
- * fastboot.
- */
-#define LP_PARTITION_ATTR_UPDATED (1 << 2)
-
-/* Mask that defines all valid attributes. When changing this, make sure to
- * update ParseMetadata().
- */
-#define LP_PARTITION_ATTRIBUTE_MASK_V0 \
-    (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
-#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTR_UPDATED)
-#define LP_PARTITION_ATTRIBUTE_MASK LP_PARTITION_ATTRIBUTE_MASK_V1
+/* 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:
diff --git a/fs_mgr/liblp/include/liblp/mock_property_fetcher.h b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
deleted file mode 100644
index f15611b..0000000
--- a/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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 <gmock/gmock.h>
-
-#include <liblp/property_fetcher.h>
-
-namespace android {
-namespace fs_mgr {
-namespace testing {
-
-class MockPropertyFetcher : public IPropertyFetcher {
-  public:
-    MOCK_METHOD2(GetProperty, std::string(const std::string&, const std::string&));
-    MOCK_METHOD2(GetBoolProperty, bool(const std::string&, bool));
-
-    // By default, return default_value for all functions.
-    MockPropertyFetcher() {
-        using ::testing::_;
-        using ::testing::Invoke;
-        ON_CALL(*this, GetProperty(_, _)).WillByDefault(Invoke([](const auto&, const auto& def) {
-            return def;
-        }));
-        ON_CALL(*this, GetBoolProperty(_, _)).WillByDefault(Invoke([](const auto&, auto def) {
-            return def;
-        }));
-    }
-};
-
-static inline void ResetMockPropertyFetcher() {
-    IPropertyFetcher::OverrideForTesting(
-            std::make_unique<::testing::NiceMock<MockPropertyFetcher>>());
-}
-
-static inline MockPropertyFetcher* GetMockedPropertyFetcher() {
-    return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
-}
-
-}  // namespace testing
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
index 7c9100b..e506bd5 100644
--- a/fs_mgr/liblp/include/liblp/partition_opener.h
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -62,11 +62,6 @@
     // 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;
-
-    // Return a path that can be used to pass the block device to device-mapper.
-    // This must either result in an absolute path, or a major:minor device
-    // sequence.
-    virtual std::string GetDeviceString(const std::string& partition_name) const = 0;
 };
 
 // Helper class to implement IPartitionOpener. If |partition_name| is not an
@@ -76,7 +71,6 @@
     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;
-    virtual std::string GetDeviceString(const std::string& partition_name) const override;
 };
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/include/liblp/property_fetcher.h b/fs_mgr/liblp/include/liblp/property_fetcher.h
deleted file mode 100644
index e73a1f5..0000000
--- a/fs_mgr/liblp/include/liblp/property_fetcher.h
+++ /dev/null
@@ -1,42 +0,0 @@
-//
-// 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>
-
-namespace android {
-namespace fs_mgr {
-
-class IPropertyFetcher {
-  public:
-    virtual ~IPropertyFetcher() = default;
-    virtual std::string GetProperty(const std::string& key, const std::string& defaultValue) = 0;
-    virtual bool GetBoolProperty(const std::string& key, bool defaultValue) = 0;
-
-    static IPropertyFetcher* GetInstance();
-    static void OverrideForTesting(std::unique_ptr<IPropertyFetcher>&&);
-};
-
-class PropertyFetcher : public IPropertyFetcher {
-  public:
-    ~PropertyFetcher() = default;
-    std::string GetProperty(const std::string& key, const std::string& defaultValue) override;
-    bool GetBoolProperty(const std::string& key, bool defaultValue) override;
-};
-
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 22f6746..fcef1f0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -21,12 +21,12 @@
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
-#include <gmock/gmock.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
 
 #include "images.h"
-#include "liblp_test.h"
 #include "reader.h"
 #include "test_partition_opener.h"
 #include "utility.h"
@@ -34,9 +34,6 @@
 
 using namespace std;
 using namespace android::fs_mgr;
-using namespace android::fs_mgr::testing;
-using ::testing::_;
-using ::testing::Return;
 using unique_fd = android::base::unique_fd;
 
 // Our tests assume a 128KiB disk with two 512 byte metadata slots.
@@ -123,7 +120,7 @@
 }
 
 // Test that our CreateFakeDisk() function works.
-TEST_F(LiblpTest, CreateFakeDisk) {
+TEST(liblp, CreateFakeDisk) {
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
@@ -139,7 +136,7 @@
 
 // Flashing metadata should not work if the metadata was created for a larger
 // disk than the destination disk.
-TEST_F(LiblpTest, ExportDiskTooSmall) {
+TEST(liblp, ExportDiskTooSmall) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
@@ -156,7 +153,7 @@
 }
 
 // Test the basics of flashing a partition and reading it back.
-TEST_F(LiblpTest, FlashAndReadback) {
+TEST(liblp, FlashAndReadback) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -206,7 +203,7 @@
 }
 
 // Test that we can update metadata slots without disturbing others.
-TEST_F(LiblpTest, UpdateAnyMetadataSlot) {
+TEST(liblp, UpdateAnyMetadataSlot) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -251,7 +248,7 @@
     }
 }
 
-TEST_F(LiblpTest, InvalidMetadataSlot) {
+TEST(liblp, InvalidMetadataSlot) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -270,7 +267,7 @@
 
 // Test that updating a metadata slot does not allow it to be computed based
 // on mismatching geometry.
-TEST_F(LiblpTest, NoChangingGeometry) {
+TEST(liblp, NoChangingGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -299,7 +296,7 @@
 }
 
 // Test that changing one bit of metadata is enough to break the checksum.
-TEST_F(LiblpTest, BitFlipGeometry) {
+TEST(liblp, BitFlipGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -318,7 +315,7 @@
     EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
 }
 
-TEST_F(LiblpTest, ReadBackupGeometry) {
+TEST(liblp, ReadBackupGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -338,7 +335,7 @@
     EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
 }
 
-TEST_F(LiblpTest, ReadBackupMetadata) {
+TEST(liblp, ReadBackupMetadata) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -365,7 +362,7 @@
 
 // Test that we don't attempt to write metadata if it would overflow its
 // reserved space.
-TEST_F(LiblpTest, TooManyPartitions) {
+TEST(liblp, TooManyPartitions) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
 
@@ -419,7 +416,7 @@
 }
 
 // Test that we can read and write image files.
-TEST_F(LiblpTest, ImageFiles) {
+TEST(liblp, ImageFiles) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -435,7 +432,7 @@
 }
 
 // Test that we can read images from buffers.
-TEST_F(LiblpTest, ImageFilesInMemory) {
+TEST(liblp, ImageFilesInMemory) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -495,7 +492,7 @@
 
 // Test that an interrupted flash operation on the "primary" copy of metadata
 // is not fatal.
-TEST_F(LiblpTest, UpdatePrimaryMetadataFailure) {
+TEST(liblp, UpdatePrimaryMetadataFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -523,7 +520,7 @@
 
 // Test that an interrupted flash operation on the "backup" copy of metadata
 // is not fatal.
-TEST_F(LiblpTest, UpdateBackupMetadataFailure) {
+TEST(liblp, UpdateBackupMetadataFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -552,7 +549,7 @@
 // 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_F(LiblpTest, UpdateMetadataCleanFailure) {
+TEST(liblp, UpdateMetadataCleanFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -589,7 +586,7 @@
 }
 
 // Test that writing a sparse image can be read back.
-TEST_F(LiblpTest, FlashSparseImage) {
+TEST(liblp, FlashSparseImage) {
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
@@ -623,7 +620,7 @@
     ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
 }
 
-TEST_F(LiblpTest, AutoSlotSuffixing) {
+TEST(liblp, AutoSlotSuffixing) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -666,10 +663,7 @@
     EXPECT_EQ(metadata->groups[1].flags, 0);
 }
 
-TEST_F(LiblpTest, UpdateRetrofit) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
-            .WillByDefault(Return(true));
-
+TEST(liblp, UpdateRetrofit) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -698,10 +692,7 @@
     ASSERT_TRUE(updated->extents.empty());
 }
 
-TEST_F(LiblpTest, UpdateNonRetrofit) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
-            .WillByDefault(Return(false));
-
+TEST(liblp, UpdateNonRetrofit) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -714,31 +705,18 @@
     EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
 }
 
-TEST_F(LiblpTest, UpdateVirtualAB) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
-    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_TRUE(UpdatePartitionTable(opener, "super", *updated.get(), 1));
-
-    // Validate old slot.
-    auto metadata = ReadMetadata(opener, "super", 0);
+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);
-    ASSERT_EQ(metadata->header.minor_version, 0);
-    ASSERT_GE(metadata->partitions.size(), 1);
-    ASSERT_EQ(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
 
-    // Validate new slot.
-    metadata = ReadMetadata(opener, "super", 1);
-    ASSERT_NE(metadata, nullptr);
-    ASSERT_EQ(metadata->header.minor_version, 1);
-    ASSERT_GE(metadata->partitions.size(), 1);
-    ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
+    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/liblp_test.h b/fs_mgr/liblp/liblp_test.h
deleted file mode 100644
index c61aaa5..0000000
--- a/fs_mgr/liblp/liblp_test.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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 <gtest/gtest.h>
-
-#include <liblp/mock_property_fetcher.h>
-
-namespace android {
-namespace fs_mgr {
-namespace testing {
-
-class LiblpTest : public ::testing::Test {
-  public:
-    void SetUp() override { ResetMockPropertyFetcher(); }
-    void TearDown() override { ResetMockPropertyFetcher(); }
-};
-
-}  // namespace testing
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
deleted file mode 100644
index d9ee12e..0000000
--- a/fs_mgr/liblp/liblp_test.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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 liblp_test">
-    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
-    </target_preparer>
-    <option name="test-suite-tag" value="vts-core" />
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="liblp_test" />
-    </test>
-</configuration>
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 1d4db85..bb8ec9c 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -26,7 +26,6 @@
 #include <unistd.h>
 
 #include <android-base/file.h>
-#include <android-base/strings.h>
 
 #include "utility.h"
 
@@ -38,24 +37,10 @@
 namespace {
 
 std::string GetPartitionAbsolutePath(const std::string& path) {
-    if (android::base::StartsWith(path, "/")) {
+    if (path[0] == '/') {
         return path;
     }
-
-    auto by_name = "/dev/block/by-name/" + path;
-    if (access(by_name.c_str(), F_OK) != 0) {
-        // If the by-name symlink doesn't exist, as a special case we allow
-        // certain devices to be used as partition names. This can happen if a
-        // Dynamic System Update is installed to an sdcard, which won't be in
-        // the boot device list.
-        //
-        // We whitelist because most devices in /dev/block are not valid for
-        // storing fiemaps.
-        if (android::base::StartsWith(path, "mmcblk")) {
-            return "/dev/block/" + path;
-        }
-    }
-    return by_name;
+    return "/dev/block/by-name/" + path;
 }
 
 bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
@@ -78,12 +63,6 @@
         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;
@@ -114,9 +93,5 @@
     return GetBlockDeviceInfo(path, info);
 }
 
-std::string PartitionOpener::GetDeviceString(const std::string& partition_name) const {
-    return GetPartitionAbsolutePath(partition_name);
-}
-
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/property_fetcher.cpp b/fs_mgr/liblp/property_fetcher.cpp
deleted file mode 100644
index 038ef4d..0000000
--- a/fs_mgr/liblp/property_fetcher.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 "liblp/property_fetcher.h"
-
-#include <memory>
-
-#include <android-base/properties.h>
-
-namespace android {
-namespace fs_mgr {
-
-std::string PropertyFetcher::GetProperty(const std::string& key, const std::string& default_value) {
-    return android::base::GetProperty(key, default_value);
-}
-
-bool PropertyFetcher::GetBoolProperty(const std::string& key, bool default_value) {
-    return android::base::GetBoolProperty(key, default_value);
-}
-
-static std::unique_ptr<IPropertyFetcher>* GetInstanceAllocation() {
-    static std::unique_ptr<IPropertyFetcher> instance = std::make_unique<PropertyFetcher>();
-    return &instance;
-}
-
-IPropertyFetcher* IPropertyFetcher::GetInstance() {
-    return GetInstanceAllocation()->get();
-}
-
-void IPropertyFetcher::OverrideForTesting(std::unique_ptr<IPropertyFetcher>&& fetcher) {
-    GetInstanceAllocation()->swap(fetcher);
-    fetcher.reset();
-}
-
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index aecf685..dcee6d2d 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -18,7 +18,6 @@
 
 #include <stddef.h>
 #include <stdlib.h>
-#include <string.h>
 #include <unistd.h>
 
 #include <functional>
@@ -181,7 +180,7 @@
     }
     // Check that the version is compatible.
     if (header.major_version != LP_METADATA_MAJOR_VERSION ||
-        header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {
+        header.minor_version > LP_METADATA_MINOR_VERSION) {
         LERROR << "Logical partition metadata has incompatible version.";
         return false;
     }
@@ -245,13 +244,6 @@
         return nullptr;
     }
 
-    uint32_t valid_attributes = 0;
-    if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
-        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V1;
-    } else {
-        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
-    }
-
     // ValidateTableSize ensured that |cursor| is valid for the number of
     // entries in the table.
     uint8_t* cursor = buffer.get() + header.partitions.offset;
@@ -260,7 +252,7 @@
         memcpy(&partition, cursor, sizeof(partition));
         cursor += header.partitions.entry_size;
 
-        if (partition.attributes & ~valid_attributes) {
+        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
             LERROR << "Logical partition has invalid attribute set.";
             return nullptr;
         }
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 48c5c83..72a3c57 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -24,10 +24,6 @@
 #include <sys/ioctl.h>
 #endif
 
-#include <map>
-#include <string>
-#include <vector>
-
 #include <android-base/file.h>
 #include <ext4_utils/ext4_utils.h>
 #include <openssl/sha.h>
@@ -139,24 +135,6 @@
     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 "";
@@ -186,14 +164,6 @@
     return true;
 }
 
-bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name) {
-    if (name.size() > sizeof(partition->name)) {
-        return false;
-    }
-    strncpy(partition->name, name.c_str(), sizeof(partition->name));
-    return true;
-}
-
 bool SetBlockReadonly(int fd, bool readonly) {
 #if defined(__linux__)
     int val = readonly;
@@ -205,9 +175,9 @@
 #endif
 }
 
-base::unique_fd GetControlFileOrOpen(std::string_view path, int flags) {
+base::unique_fd GetControlFileOrOpen(const char* path, int flags) {
 #if defined(__ANDROID__)
-    int fd = android_get_control_file(path.data());
+    int fd = android_get_control_file(path);
     if (fd >= 0) {
         int newfd = TEMP_FAILURE_RETRY(dup(fd));
         if (newfd >= 0) {
@@ -216,73 +186,7 @@
         PERROR << "Cannot dup fd for already controlled file: " << path << ", reopening...";
     }
 #endif
-    return base::unique_fd(open(path.data(), flags));
-}
-
-bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
-                                      uint32_t target_slot_number) {
-    std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
-    std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
-
-    // There can be leftover groups with target suffix on retrofit devices.
-    // They are useless now, so delete.
-    std::vector<LpMetadataPartitionGroup*> new_group_ptrs;
-    for (auto& group : metadata->groups) {
-        std::string group_name = GetPartitionGroupName(group);
-        std::string slot_suffix = GetPartitionSlotSuffix(group_name);
-        // Don't add groups with target slot suffix.
-        if (slot_suffix == target_slot_suffix) continue;
-        // Replace source slot suffix with target slot suffix.
-        if (slot_suffix == source_slot_suffix) {
-            std::string new_name = group_name.substr(0, group_name.size() - slot_suffix.size()) +
-                                   target_slot_suffix;
-            if (!UpdatePartitionGroupName(&group, new_name)) {
-                LERROR << "Group name too long: " << new_name;
-                return false;
-            }
-        }
-        new_group_ptrs.push_back(&group);
-    }
-
-    std::vector<LpMetadataPartition*> new_partition_ptrs;
-    for (auto& partition : metadata->partitions) {
-        std::string partition_name = GetPartitionName(partition);
-        std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
-        // Don't add partitions with target slot suffix.
-        if (slot_suffix == target_slot_suffix) continue;
-        // Replace source slot suffix with target slot suffix.
-        if (slot_suffix == source_slot_suffix) {
-            std::string new_name =
-                    partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
-                    target_slot_suffix;
-            if (!UpdatePartitionName(&partition, new_name)) {
-                LERROR << "Partition name too long: " << new_name;
-                return false;
-            }
-        }
-        // Update group index.
-        auto it = std::find(new_group_ptrs.begin(), new_group_ptrs.end(),
-                            &metadata->groups[partition.group_index]);
-        if (it == new_group_ptrs.end()) {
-            LWARN << "Removing partition " << partition_name << " from group "
-                  << GetPartitionGroupName(metadata->groups[partition.group_index])
-                  << "; this partition should not belong to this group!";
-            continue;  // not adding to new_partition_ptrs
-        }
-        partition.attributes |= LP_PARTITION_ATTR_UPDATED;
-        partition.group_index = std::distance(new_group_ptrs.begin(), it);
-        new_partition_ptrs.push_back(&partition);
-    }
-
-    std::vector<LpMetadataPartition> new_partitions;
-    for (auto* p : new_partition_ptrs) new_partitions.emplace_back(std::move(*p));
-    metadata->partitions = std::move(new_partitions);
-
-    std::vector<LpMetadataPartitionGroup> new_groups;
-    for (auto* g : new_group_ptrs) new_groups.emplace_back(std::move(*g));
-    metadata->groups = std::move(new_groups);
-
-    return true;
+    return base::unique_fd(open(path, flags));
 }
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 0661769..96f1717 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,9 +21,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <string>
-#include <string_view>
-
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
@@ -92,16 +89,11 @@
 // Update names from C++ strings.
 bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
 bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
-bool UpdatePartitionName(LpMetadataPartition* partition, 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(std::string_view path, int flags);
-
-// For Virtual A/B updates, modify |metadata| so that it can be written to |target_slot_number|.
-bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
-                                      uint32_t target_slot_number);
+::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index cac3989..15f7fff 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <gtest/gtest.h>
-#include <liblp/builder.h>
 #include <liblp/liblp.h>
 
 #include "utility.h"
@@ -76,81 +75,3 @@
     EXPECT_EQ(GetPartitionSlotSuffix("system_a"), "_a");
     EXPECT_EQ(GetPartitionSlotSuffix("system_b"), "_b");
 }
-
-namespace android {
-namespace fs_mgr {
-// Equality comparison for testing. In reality, equality of device_index doesn't
-// necessary mean equality of the block device.
-bool operator==(const LinearExtent& l, const LinearExtent& r) {
-    return l.device_index() == r.device_index() && l.physical_sector() == r.physical_sector() &&
-           l.end_sector() == r.end_sector();
-}
-}  // namespace fs_mgr
-}  // namespace android
-
-static std::vector<LinearExtent> GetPartitionExtents(Partition* p) {
-    std::vector<LinearExtent> extents;
-    for (auto&& extent : p->extents()) {
-        auto linear_extent = extent->AsLinearExtent();
-        if (!linear_extent) return {};
-        extents.push_back(*linear_extent);
-    }
-    return extents;
-}
-
-TEST(liblp, UpdateMetadataForInPlaceSnapshot) {
-    using std::unique_ptr;
-
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
-    ASSERT_NE(builder, nullptr);
-
-    ASSERT_TRUE(builder->AddGroup("group_a", 256 * 1024));
-    Partition* system_a = builder->AddPartition("system_a", "group_a", LP_PARTITION_ATTR_READONLY);
-    ASSERT_NE(system_a, nullptr);
-    ASSERT_TRUE(builder->ResizePartition(system_a, 40 * 1024));
-    Partition* vendor_a = builder->AddPartition("vendor_a", "group_a", LP_PARTITION_ATTR_READONLY);
-    ASSERT_NE(vendor_a, nullptr);
-    ASSERT_TRUE(builder->ResizePartition(vendor_a, 20 * 1024));
-
-    ASSERT_TRUE(builder->AddGroup("group_b", 258 * 1024));
-    Partition* system_b = builder->AddPartition("system_b", "group_b", LP_PARTITION_ATTR_READONLY);
-    ASSERT_NE(system_b, nullptr);
-    ASSERT_TRUE(builder->ResizePartition(system_b, 36 * 1024));
-    Partition* vendor_b = builder->AddPartition("vendor_b", "group_b", LP_PARTITION_ATTR_READONLY);
-    ASSERT_NE(vendor_b, nullptr);
-    ASSERT_TRUE(builder->ResizePartition(vendor_b, 32 * 1024));
-
-    auto system_a_extents = GetPartitionExtents(system_a);
-    ASSERT_FALSE(system_a_extents.empty());
-
-    auto vendor_a_extents = GetPartitionExtents(vendor_a);
-    ASSERT_FALSE(vendor_a_extents.empty());
-
-    auto metadata = builder->Export();
-    ASSERT_NE(nullptr, metadata);
-
-    ASSERT_TRUE(UpdateMetadataForInPlaceSnapshot(metadata.get(), 0, 1));
-
-    auto new_builder = MetadataBuilder::New(*metadata);
-    ASSERT_NE(nullptr, new_builder);
-
-    EXPECT_EQ(nullptr, new_builder->FindGroup("group_a"));
-    EXPECT_EQ(nullptr, new_builder->FindPartition("system_a"));
-    EXPECT_EQ(nullptr, new_builder->FindPartition("vendor_a"));
-
-    auto group_b = new_builder->FindGroup("group_b");
-    ASSERT_NE(nullptr, group_b);
-    ASSERT_EQ(256 * 1024, group_b->maximum_size());
-
-    auto new_system_b = new_builder->FindPartition("system_b");
-    ASSERT_NE(nullptr, new_system_b);
-    EXPECT_EQ(40 * 1024, new_system_b->size());
-    auto new_system_b_extents = GetPartitionExtents(new_system_b);
-    EXPECT_EQ(system_a_extents, new_system_b_extents);
-
-    auto new_vendor_b = new_builder->FindPartition("vendor_b");
-    ASSERT_NE(nullptr, new_vendor_b);
-    EXPECT_EQ(20 * 1024, new_vendor_b->size());
-    auto new_vendor_b_extents = GetPartitionExtents(new_vendor_b);
-    EXPECT_EQ(vendor_a_extents, new_vendor_b_extents);
-}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 8a983ad..bffcb7e 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -17,7 +17,6 @@
 #include "writer.h"
 
 #include <inttypes.h>
-#include <string.h>
 #include <unistd.h>
 
 #include <string>
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
deleted file mode 100644
index 9256a16..0000000
--- a/fs_mgr/libsnapshot/Android.bp
+++ /dev/null
@@ -1,183 +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.
-//
-
-cc_defaults {
-    name: "libsnapshot_defaults",
-    defaults: ["fs_mgr_defaults"],
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-        "liblp",
-    ],
-    static_libs: [
-        "libcutils",
-        "libdm",
-        "libfs_mgr",
-        "libfstab",
-        "liblp",
-        "update_metadata-protos",
-    ],
-    whole_static_libs: [
-        "libext2_uuid",
-        "libext4_utils",
-        "libfstab",
-    ],
-    header_libs: [
-        "libfiemap_headers",
-    ],
-    export_static_lib_headers: [
-        "update_metadata-protos",
-    ],
-    export_header_lib_headers: [
-        "libfiemap_headers",
-    ],
-    export_include_dirs: ["include"],
-    proto: {
-        type: "lite",
-        export_proto_headers: true,
-        canonical_path_from_root: false,
-    },
-}
-
-cc_defaults {
-    name: "libsnapshot_hal_deps",
-    cflags: [
-        "-DLIBSNAPSHOT_USE_HAL",
-    ],
-    shared_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
-    ],
-}
-
-filegroup {
-    name: "libsnapshot_sources",
-    srcs: [
-        "android/snapshot/snapshot.proto",
-        "snapshot.cpp",
-        "snapshot_metadata_updater.cpp",
-        "partition_cow_creator.cpp",
-        "utility.cpp",
-    ],
-}
-
-cc_library_headers {
-    name: "libsnapshot_headers",
-    recovery_available: true,
-    defaults: ["libsnapshot_defaults"],
-}
-
-cc_library_static {
-    name: "libsnapshot",
-    defaults: [
-        "libsnapshot_defaults",
-        "libsnapshot_hal_deps",
-    ],
-    srcs: [":libsnapshot_sources"],
-    whole_static_libs: [
-        "libfiemap_binder",
-    ],
-}
-
-cc_library_static {
-    name: "libsnapshot_init",
-    defaults: ["libsnapshot_defaults"],
-    srcs: [":libsnapshot_sources"],
-    recovery_available: true,
-    whole_static_libs: [
-        "libfiemap_passthrough",
-    ],
-}
-
-cc_library_static {
-    name: "libsnapshot_nobinder",
-    defaults: [
-        "libsnapshot_defaults",
-        "libsnapshot_hal_deps",
-    ],
-    srcs: [":libsnapshot_sources"],
-    recovery_available: true,
-    whole_static_libs: [
-        "libfiemap_passthrough",
-    ],
-}
-
-cc_test {
-    name: "libsnapshot_test",
-    defaults: ["libsnapshot_defaults"],
-    srcs: [
-        "snapshot_test.cpp",
-        "partition_cow_creator_test.cpp",
-        "snapshot_metadata_updater_test.cpp",
-        "test_helpers.cpp",
-    ],
-    shared_libs: [
-        "libbinder",
-        "libcrypto",
-        "libhidlbase",
-        "libprotobuf-cpp-lite",
-        "libutils",
-    ],
-    static_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
-        "libfs_mgr",
-        "libgmock",
-        "liblp",
-        "libsnapshot",
-        "libsparse",
-        "libz",
-    ],
-    header_libs: [
-        "libstorage_literals_headers",
-    ],
-}
-
-cc_binary {
-    name: "snapshotctl",
-    srcs: [
-        "snapshotctl.cpp",
-    ],
-    static_libs: [
-        "libdm",
-        "libext2_uuid",
-        "libfiemap_binder",
-        "libfstab",
-        "libsnapshot",
-    ],
-    shared_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
-        "libbase",
-        "libbinder",
-        "libbinderthreadstate",
-        "libext4_utils",
-        "libfs_mgr",
-        "libhidlbase",
-        "liblog",
-        "liblp",
-        "libprotobuf-cpp-lite",
-        "libutils",
-    ],
-    init_rc: [
-        "snapshotctl.rc",
-    ],
-}
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
deleted file mode 100644
index 0cfa7e4..0000000
--- a/fs_mgr/libsnapshot/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-dvander@google.com
-elsk@google.com
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
deleted file mode 100644
index 629c3a4..0000000
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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.snapshot;
-
-option optimize_for = LITE_RUNTIME;
-
-// Next: 4
-enum SnapshotState {
-    // No snapshot is found.
-    NONE = 0;
-
-    // The snapshot has been created and possibly written to. Rollbacks are
-    // possible by destroying the snapshot.
-    CREATED = 1;
-
-    // Changes are being merged. No rollbacks are possible beyond this point.
-    MERGING = 2;
-
-    // Changes have been merged, Future reboots may map the base device
-    // directly.
-    MERGE_COMPLETED = 3;
-}
-
-// Next: 9
-message SnapshotStatus {
-    // Name of the snapshot. This is usually the name of the snapshotted
-    // logical partition; for example, "system_b".
-    string name = 1;
-
-    SnapshotState state = 2;
-
-    // Size of the full (base) device.
-    uint64 device_size = 3;
-
-    // Size of the snapshot. This is the sum of lengths of ranges in the base
-    // device that needs to be snapshotted during the update.
-    // This must be less than or equal to |device_size|.
-    // This value is 0 if no snapshot is needed for this device because
-    // no changes
-    uint64 snapshot_size = 4;
-
-    // Size of the "COW partition". A COW partition is a special logical
-    // partition represented in the super partition metadata. This partition and
-    // the "COW image" form the "COW device" that supports the snapshot device.
-    //
-    // When SnapshotManager creates a COW device, it first searches for unused
-    // blocks in the super partition, and use those before creating the COW
-    // image if the COW partition is not big enough.
-    //
-    // This value is 0 if no space in super is left for the COW partition.
-    // |cow_partition_size + cow_file_size| must not be zero if |snapshot_size|
-    // is non-zero.
-    uint64 cow_partition_size = 5;
-
-    // Size of the "COW file", or "COW image". A COW file / image is created
-    // when the "COW partition" is not big enough to store changes to the
-    // snapshot device.
-    //
-    // This value is 0 if |cow_partition_size| is big enough to hold all changes
-    // to the snapshot device.
-    uint64 cow_file_size = 6;
-
-    // Sectors allocated for the COW device. Recording this value right after
-    // the update and before the merge allows us to infer the progress of the
-    // merge process.
-    // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
-    uint64 sectors_allocated = 7;
-
-    // Metadata sectors allocated for the COW device. Recording this value right
-    // before the update and before the merge allows us to infer the progress of
-    // the merge process.
-    // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
-    uint64 metadata_sectors = 8;
-}
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
deleted file mode 100644
index fef256d..0000000
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ /dev/null
@@ -1,135 +0,0 @@
-// 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 <vector>
-
-namespace android {
-namespace snapshot {
-
-class DmSnapCowSizeCalculator {
-  public:
-    DmSnapCowSizeCalculator(unsigned int sector_bytes, unsigned int chunk_sectors)
-        : sector_bytes_(sector_bytes),
-          chunk_sectors_(chunk_sectors),
-          exceptions_per_chunk(chunk_sectors_ * sector_bytes_ / (64 * 2 / 8)) {}
-
-    void WriteByte(uint64_t address) { WriteSector(address / sector_bytes_); }
-    void WriteSector(uint64_t sector) { WriteChunk(sector / chunk_sectors_); }
-    void WriteChunk(uint64_t chunk_id) {
-        if (modified_chunks_.size() <= chunk_id) {
-            modified_chunks_.resize(chunk_id + 1, false);
-        }
-        modified_chunks_[chunk_id] = true;
-    }
-
-    uint64_t cow_size_bytes() const { return cow_size_sectors() * sector_bytes_; }
-    uint64_t cow_size_sectors() const { return cow_size_chunks() * chunk_sectors_; }
-
-    /*
-     * The COW device has a precise internal structure as follows:
-     *
-     * - header (1 chunk)
-     * - #0 map and chunks
-     *   - map (1 chunk)
-     *   - chunks addressable by previous map (exceptions_per_chunk)
-     * - #1 map and chunks
-     *   - map (1 chunk)
-     *   - chunks addressable by previous map (exceptions_per_chunk)
-     * ...
-     * - #n: map and chunks
-     *   - map (1 chunk)
-     *   - chunks addressable by previous map (exceptions_per_chunk)
-     * - 1 extra chunk
-     */
-    uint64_t cow_size_chunks() const {
-        uint64_t modified_chunks_count = 0;
-        uint64_t cow_chunks = 0;
-
-        for (const auto& c : modified_chunks_) {
-            if (c) {
-                ++modified_chunks_count;
-            }
-        }
-
-        /* disk header + padding = 1 chunk */
-        cow_chunks += 1;
-
-        /* snapshot modified chunks */
-        cow_chunks += modified_chunks_count;
-
-        /* snapshot chunks index metadata */
-        cow_chunks += 1 + modified_chunks_count / exceptions_per_chunk;
-
-        return cow_chunks;
-    }
-
-  private:
-    /*
-     * Size of each sector in bytes.
-     */
-    const uint64_t sector_bytes_;
-
-    /*
-     * Size of each chunk in sectors.
-     */
-    const uint64_t chunk_sectors_;
-
-    /*
-     * The COW device stores tables to map the modified chunks. Each table
-     * has the size of exactly 1 chunk.
-     * Each row of the table (also called exception in the kernel) contains two
-     * 64 bit indices to identify the corresponding chunk, and this 128 bit row
-     * size is a constant.
-     * The number of exceptions that each table can contain determines the
-     * number of data chunks that separate two consecutive tables. This value
-     * is then fundamental to compute the space overhead introduced by the
-     * tables in COW devices.
-     */
-    const uint64_t exceptions_per_chunk;
-
-    /*
-     * |modified_chunks_| is a container that keeps trace of the modified
-     * chunks.
-     * Multiple options were considered when choosing the most appropriate data
-     * structure for this container. Here follows a summary of why vector<bool>
-     * has been chosen, taking as a reference a snapshot partition of 4 GiB and
-     * chunk size of 4 KiB.
-     * - std::set<uint64_t> is very space-efficient for a small number of
-     *   operations, but if the whole snapshot is changed, it would need to
-     *   store
-     *     4 GiB / 4 KiB * (64 bit / 8) = 8 MiB
-     *   just for the data, plus the additional data overhead for the red-black
-     *   tree used for data sorting (if each rb-tree element stores 3 address
-     *   and the word-aligne color, the total size grows to 32 MiB).
-     * - std::bitset<N> is not a good fit because requires a priori knowledge,
-     *   at compile time, of the bitset size.
-     * - std::vector<bool> is a special case of vector, which uses a data
-     *   compression that allows reducing the space utilization of each element
-     *   to 1 bit. In detail, this data structure is composed of a resizable
-     *   array of words, each of them representing a bitmap. On a 64 bit
-     *   device, modifying the whole 4 GiB snapshot grows this container up to
-     *     4 * GiB / 4 KiB / 64 = 64 KiB
-     *   that, even if is the same space requirement to change a single byte at
-     *   the highest address of the snapshot, is a very affordable space
-     *   requirement.
-     */
-    std::vector<bool> modified_chunks_;
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h b/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
deleted file mode 100644
index d5ceb0e..0000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/auto_device.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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 <android-base/macros.h>
-
-namespace android {
-namespace snapshot {
-
-// An abstract "device" that will be cleaned up (unmapped, unmounted, etc.) upon
-// destruction.
-struct AutoDevice {
-    virtual ~AutoDevice(){};
-    void Release();
-
-  protected:
-    AutoDevice(const std::string& name) : name_(name) {}
-    std::string name_;
-
-  private:
-    DISALLOW_COPY_AND_ASSIGN(AutoDevice);
-    AutoDevice(AutoDevice&& other) = delete;
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
deleted file mode 100644
index c820395..0000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ /dev/null
@@ -1,480 +0,0 @@
-// 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 <chrono>
-#include <map>
-#include <memory>
-#include <optional>
-#include <ostream>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-#include <fs_mgr_dm_linear.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/builder.h>
-#include <liblp/liblp.h>
-#include <update_engine/update_metadata.pb.h>
-
-#include <libsnapshot/auto_device.h>
-
-#ifndef FRIEND_TEST
-#define FRIEND_TEST(test_set_name, individual_test) \
-    friend class test_set_name##_##individual_test##_Test
-#define DEFINED_FRIEND_TEST
-#endif
-
-namespace android {
-
-namespace fiemap {
-class IImageManager;
-}  // namespace fiemap
-
-namespace fs_mgr {
-struct CreateLogicalPartitionParams;
-class IPartitionOpener;
-}  // namespace fs_mgr
-
-// Forward declare IBootControl types since we cannot include only the headers
-// with Soong. Note: keep the enum width in sync.
-namespace hardware {
-namespace boot {
-namespace V1_1 {
-enum class MergeStatus : int32_t;
-}  // namespace V1_1
-}  // namespace boot
-}  // namespace hardware
-
-namespace snapshot {
-
-struct AutoDeleteCowImage;
-struct AutoDeleteSnapshot;
-struct AutoDeviceList;
-struct PartitionCowCreator;
-class SnapshotStatus;
-
-static constexpr const std::string_view kCowGroupName = "cow";
-
-enum class UpdateState : unsigned int {
-    // No update or merge is in progress.
-    None,
-
-    // An update is applying; snapshots may already exist.
-    Initiated,
-
-    // An update is pending, but has not been successfully booted yet.
-    Unverified,
-
-    // The kernel is merging in the background.
-    Merging,
-
-    // Post-merge cleanup steps could not be completed due to a transient
-    // error, but the next reboot will finish any pending operations.
-    MergeNeedsReboot,
-
-    // Merging is complete, and needs to be acknowledged.
-    MergeCompleted,
-
-    // Merging failed due to an unrecoverable error.
-    MergeFailed,
-
-    // The update was implicitly cancelled, either by a rollback or a flash
-    // operation via fastboot. This state can only be returned by WaitForMerge.
-    Cancelled
-};
-std::ostream& operator<<(std::ostream& os, UpdateState state);
-
-class SnapshotManager final {
-    using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
-    using IPartitionOpener = android::fs_mgr::IPartitionOpener;
-    using LpMetadata = android::fs_mgr::LpMetadata;
-    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
-    using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
-    using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
-
-  public:
-    // Dependency injection for testing.
-    class IDeviceInfo {
-      public:
-        virtual ~IDeviceInfo() {}
-        virtual std::string GetGsidDir() const = 0;
-        virtual std::string GetMetadataDir() const = 0;
-        virtual std::string GetSlotSuffix() const = 0;
-        virtual std::string GetOtherSlotSuffix() const = 0;
-        virtual std::string GetSuperDevice(uint32_t slot) const = 0;
-        virtual const IPartitionOpener& GetPartitionOpener() const = 0;
-        virtual bool IsOverlayfsSetup() const = 0;
-        virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
-        virtual bool IsRecovery() const = 0;
-    };
-
-    ~SnapshotManager();
-
-    // Return a new SnapshotManager instance, or null on error. The device
-    // pointer is owned for the lifetime of SnapshotManager. If null, a default
-    // instance will be created.
-    static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
-
-    // This is similar to New(), except designed specifically for first-stage
-    // init.
-    static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
-
-    // Helper function for first-stage init to check whether a SnapshotManager
-    // might be needed to perform first-stage mounts.
-    static bool IsSnapshotManagerNeeded();
-
-    // Begin an update. This must be called before creating any snapshots. It
-    // will fail if GetUpdateState() != None.
-    bool BeginUpdate();
-
-    // Cancel an update; any snapshots will be deleted. This is allowed if the
-    // state == Initiated, None, or Unverified (before rebooting to the new
-    // slot).
-    bool CancelUpdate();
-
-    // Mark snapshot writes as having completed. After this, new snapshots cannot
-    // be created, and the device must either cancel the OTA (either before
-    // rebooting or after rolling back), or merge the OTA.
-    bool FinishedSnapshotWrites();
-
-  private:
-    // Initiate a merge on all snapshot devices. This should only be used after an
-    // update has been marked successful after booting.
-    bool InitiateMerge();
-
-    // Perform any necessary post-boot actions. This should be run soon after
-    // /data is mounted.
-    //
-    // If a merge is in progress, this function will block until the merge is
-    // completed. If a merge or update was cancelled, this will clean up any
-    // update artifacts and return.
-    //
-    // Note that after calling this, GetUpdateState() may still return that a
-    // merge is in progress:
-    //   MergeFailed indicates that a fatal error occurred. WaitForMerge() may
-    //   called any number of times again to attempt to make more progress, but
-    //   we do not expect it to succeed if a catastrophic error occurred.
-    //
-    //   MergeNeedsReboot indicates that the merge has completed, but cleanup
-    //   failed. This can happen if for some reason resources were not closed
-    //   properly. In this case another reboot is needed before we can take
-    //   another OTA. However, WaitForMerge() can be called again without
-    //   rebooting, to attempt to finish cleanup anyway.
-    //
-    //   MergeCompleted indicates that the update has fully completed.
-    //   GetUpdateState will return None, and a new update can begin.
-    UpdateState ProcessUpdateState();
-
-  public:
-    // Initiate the merge if necessary, then wait for the merge to finish.
-    // See InitiateMerge() and ProcessUpdateState() for details.
-    // Returns:
-    //   - None if no merge to initiate
-    //   - MergeCompleted if merge is completed
-    //   - other states indicating an error has occurred
-    UpdateState InitiateMergeAndWait();
-
-    // Find the status of the current update, if any.
-    //
-    // |progress| depends on the returned status:
-    //   Merging: Value in the range [0, 100]
-    //   MergeCompleted: 100
-    //   Other: 0
-    UpdateState GetUpdateState(double* progress = nullptr);
-
-    // Create necessary COW device / files for OTA clients. New logical partitions will be added to
-    // group "cow" in target_metadata. Regions of partitions of current_metadata will be
-    // "write-protected" and snapshotted.
-    bool CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);
-
-    // Map a snapshotted partition for OTA clients to write to. Write-protected regions are
-    // determined previously in CreateSnapshots.
-    bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params, std::string* snapshot_path);
-
-    // Unmap a snapshot device that's previously mapped with MapUpdateSnapshot.
-    bool UnmapUpdateSnapshot(const std::string& target_partition_name);
-
-    // If this returns true, first-stage mount must call
-    // CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
-    bool NeedSnapshotsInFirstStageMount();
-
-    // Perform first-stage mapping of snapshot targets. This replaces init's
-    // call to CreateLogicalPartitions when snapshots are present.
-    bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
-
-    // Dump debug information.
-    bool Dump(std::ostream& os);
-
-    // Ensure metadata directory is mounted in recovery. When the returned
-    // AutoDevice is destroyed, the metadata directory is automatically
-    // unmounted.
-    // Return nullptr if any failure.
-    // In Android mode, Return an AutoDevice that does nothing
-    // In recovery, return an AutoDevice that does nothing if metadata entry
-    // is not found in fstab.
-    // Note: if this function is called the second time before the AutoDevice returned from the
-    // first call is destroyed, the device will be unmounted when any of these AutoDevices is
-    // destroyed. FOr example:
-    //   auto a = mgr->EnsureMetadataMounted(); // mounts
-    //   auto b = mgr->EnsureMetadataMounted(); // does nothing
-    //   b.reset() // unmounts
-    //   a.reset() // does nothing
-    std::unique_ptr<AutoDevice> EnsureMetadataMounted();
-
-  private:
-    FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
-    FRIEND_TEST(SnapshotTest, CreateSnapshot);
-    FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);
-    FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);
-    FRIEND_TEST(SnapshotTest, FlashSuperDuringMerge);
-    FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate);
-    FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
-    FRIEND_TEST(SnapshotTest, MapSnapshot);
-    FRIEND_TEST(SnapshotTest, Merge);
-    FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
-    FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
-    FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
-    FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
-    friend class SnapshotTest;
-    friend class SnapshotUpdateTest;
-    friend class FlashAfterUpdateTest;
-    friend struct AutoDeleteCowImage;
-    friend struct AutoDeleteSnapshot;
-    friend struct PartitionCowCreator;
-
-    using DmTargetSnapshot = android::dm::DmTargetSnapshot;
-    using IImageManager = android::fiemap::IImageManager;
-    using TargetInfo = android::dm::DeviceMapper::TargetInfo;
-
-    explicit SnapshotManager(IDeviceInfo* info);
-
-    // This is created lazily since it can connect via binder.
-    bool EnsureImageManager();
-
-    // Helper for first-stage init.
-    bool ForceLocalImageManager();
-
-    // Helper function for tests.
-    IImageManager* image_manager() const { return images_.get(); }
-
-    // Since libsnapshot is included into multiple processes, we flock() our
-    // files for simple synchronization. LockedFile is a helper to assist with
-    // this. It also serves as a proof-of-lock for some functions.
-    class LockedFile final {
-      public:
-        LockedFile(const std::string& path, android::base::unique_fd&& fd, int lock_mode)
-            : path_(path), fd_(std::move(fd)), lock_mode_(lock_mode) {}
-        ~LockedFile();
-
-        const std::string& path() const { return path_; }
-        int fd() const { return fd_; }
-        int lock_mode() const { return lock_mode_; }
-
-      private:
-        std::string path_;
-        android::base::unique_fd fd_;
-        int lock_mode_;
-    };
-    std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
-    bool Truncate(LockedFile* file);
-
-    // Create a new snapshot record. This creates the backing COW store and
-    // persists information needed to map the device. The device can be mapped
-    // with MapSnapshot().
-    //
-    // |status|.device_size should be the size of the base_device that will be passed
-    // via MapDevice(). |status|.snapshot_size should be the number of bytes in the
-    // base device, starting from 0, that will be snapshotted. |status|.cow_file_size
-    // should be the amount of space that will be allocated to store snapshot
-    // deltas.
-    //
-    // If |status|.snapshot_size < |status|.device_size, then the device will always
-    // be mapped with two table entries: a dm-snapshot range covering
-    // snapshot_size, and a dm-linear range covering the remainder.
-    //
-    // All sizes are specified in bytes, and the device, snapshot, COW partition and COW file sizes
-    // must be a multiple of the sector size (512 bytes).
-    bool CreateSnapshot(LockedFile* lock, SnapshotStatus* status);
-
-    // |name| should be the base partition name (e.g. "system_a"). Create the
-    // backing COW image using the size previously passed to CreateSnapshot().
-    bool CreateCowImage(LockedFile* lock, const std::string& name);
-
-    // Map a snapshot device that was previously created with CreateSnapshot.
-    // If a merge was previously initiated, the device-mapper table will have a
-    // snapshot-merge target instead of a snapshot target. If the timeout
-    // parameter greater than zero, this function will wait the given amount
-    // of time for |dev_path| to become available, and fail otherwise. If
-    // timeout_ms is 0, then no wait will occur and |dev_path| may not yet
-    // exist on return.
-    bool MapSnapshot(LockedFile* lock, const std::string& name, const std::string& base_device,
-                     const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,
-                     std::string* dev_path);
-
-    // Map a COW image that was previous created with CreateCowImage.
-    std::optional<std::string> MapCowImage(const std::string& name,
-                                           const std::chrono::milliseconds& timeout_ms);
-
-    // Remove the backing copy-on-write image and snapshot states for the named snapshot. The
-    // caller is responsible for ensuring that the snapshot is unmapped.
-    bool DeleteSnapshot(LockedFile* lock, const std::string& name);
-
-    // Unmap a snapshot device previously mapped with MapSnapshotDevice().
-    bool UnmapSnapshot(LockedFile* lock, const std::string& name);
-
-    // Unmap a COW image device previously mapped with MapCowImage().
-    bool UnmapCowImage(const std::string& name);
-
-    // Unmap and remove all known snapshots.
-    bool RemoveAllSnapshots(LockedFile* lock);
-
-    // List the known snapshot names.
-    bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots);
-
-    // Check for a cancelled or rolled back merge, returning true if such a
-    // condition was detected and handled.
-    bool HandleCancelledUpdate(LockedFile* lock);
-
-    // Helper for HandleCancelledUpdate. Assumes booting from new slot.
-    bool HandleCancelledUpdateOnNewSlot(LockedFile* lock);
-
-    // Remove artifacts created by the update process, such as snapshots, and
-    // set the update state to None.
-    bool RemoveAllUpdateState(LockedFile* lock);
-
-    // Interact with /metadata/ota/state.
-    std::unique_ptr<LockedFile> OpenStateFile(int open_flags, int lock_flags);
-    std::unique_ptr<LockedFile> LockShared();
-    std::unique_ptr<LockedFile> LockExclusive();
-    UpdateState ReadUpdateState(LockedFile* file);
-    bool WriteUpdateState(LockedFile* file, UpdateState state);
-    std::string GetStateFilePath() const;
-
-    // Helpers for merging.
-    bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
-    bool RewriteSnapshotDeviceTable(const std::string& dm_name);
-    bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
-    void AcknowledgeMergeSuccess(LockedFile* lock);
-    void AcknowledgeMergeFailure();
-    std::unique_ptr<LpMetadata> ReadCurrentMetadata();
-
-    enum class MetadataPartitionState {
-        // Partition does not exist.
-        None,
-        // Partition is flashed.
-        Flashed,
-        // Partition is created by OTA client.
-        Updated,
-    };
-    // Helper function to check the state of a partition as described in metadata.
-    MetadataPartitionState GetMetadataPartitionState(const LpMetadata& metadata,
-                                                     const std::string& name);
-
-    // Note that these require the name of the device containing the snapshot,
-    // which may be the "inner" device. Use GetsnapshotDeviecName().
-    bool QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,
-                             DmTargetSnapshot::Status* status);
-    bool IsSnapshotDevice(const std::string& dm_name, TargetInfo* target = nullptr);
-
-    // Internal callback for when merging is complete.
-    bool OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
-                                 const SnapshotStatus& status);
-    bool CollapseSnapshotDevice(const std::string& name, const SnapshotStatus& status);
-
-    // Only the following UpdateStates are used here:
-    //   UpdateState::Merging
-    //   UpdateState::MergeCompleted
-    //   UpdateState::MergeFailed
-    //   UpdateState::MergeNeedsReboot
-    UpdateState CheckMergeState();
-    UpdateState CheckMergeState(LockedFile* lock);
-    UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
-
-    // Interact with status files under /metadata/ota/snapshots.
-    bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
-    bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
-    std::string GetSnapshotStatusFilePath(const std::string& name);
-
-    std::string GetSnapshotBootIndicatorPath();
-    void RemoveSnapshotBootIndicator();
-
-    // Return the name of the device holding the "snapshot" or "snapshot-merge"
-    // target. This may not be the final device presented via MapSnapshot(), if
-    // for example there is a linear segment.
-    std::string GetSnapshotDeviceName(const std::string& snapshot_name,
-                                      const SnapshotStatus& status);
-
-    // Map the base device, COW devices, and snapshot device.
-    bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
-                                  std::string* path);
-
-    // Map the COW devices, including the partition in super and the images.
-    // |params|:
-    //    - |partition_name| should be the name of the top-level partition (e.g. system_b),
-    //            not system_b-cow-img
-    //    - |device_name| and |partition| is ignored
-    //    - |timeout_ms| and the rest is respected
-    // Return the path in |cow_device_path| (e.g. /dev/block/dm-1) and major:minor in
-    // |cow_device_string|
-    bool MapCowDevices(LockedFile* lock, const CreateLogicalPartitionParams& params,
-                       const SnapshotStatus& snapshot_status, AutoDeviceList* created_devices,
-                       std::string* cow_name);
-
-    // The reverse of MapCowDevices.
-    bool UnmapCowDevices(LockedFile* lock, const std::string& name);
-
-    // The reverse of MapPartitionWithSnapshot.
-    bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
-
-    // If there isn't a previous update, return true. |needs_merge| is set to false.
-    // If there is a previous update but the device has not boot into it, tries to cancel the
-    //   update and delete any snapshots. Return true if successful. |needs_merge| is set to false.
-    // If there is a previous update and the device has boot into it, do nothing and return true.
-    //   |needs_merge| is set to true.
-    bool TryCancelUpdate(bool* needs_merge);
-
-    // Helper for CreateUpdateSnapshots.
-    // Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
-    bool CreateUpdateSnapshotsInternal(LockedFile* lock, const DeltaArchiveManifest& manifest,
-                                       PartitionCowCreator* cow_creator,
-                                       AutoDeviceList* created_devices,
-                                       std::map<std::string, SnapshotStatus>* all_snapshot_status);
-
-    // Initialize snapshots so that they can be mapped later.
-    // Map the COW partition and zero-initialize the header.
-    bool InitializeUpdateSnapshots(
-            LockedFile* lock, MetadataBuilder* target_metadata,
-            const LpMetadata* exported_target_metadata, const std::string& target_suffix,
-            const std::map<std::string, SnapshotStatus>& all_snapshot_status);
-
-    std::string gsid_dir_;
-    std::string metadata_dir_;
-    std::unique_ptr<IDeviceInfo> device_;
-    std::unique_ptr<IImageManager> images_;
-    bool has_local_image_manager_ = false;
-};
-
-}  // namespace snapshot
-}  // namespace android
-
-#ifdef DEFINED_FRIEND_TEST
-#undef DEFINED_FRIEND_TEST
-#undef FRIEND_TEST
-#endif
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
deleted file mode 100644
index 77315b4..0000000
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-// 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 "partition_cow_creator.h"
-
-#include <math.h>
-
-#include <android-base/logging.h>
-#include <android/snapshot/snapshot.pb.h>
-
-#include "dm_snapshot_internals.h"
-#include "utility.h"
-
-using android::dm::kSectorSize;
-using android::fs_mgr::Extent;
-using android::fs_mgr::Interval;
-using android::fs_mgr::kDefaultBlockSize;
-using android::fs_mgr::Partition;
-using chromeos_update_engine::InstallOperation;
-template <typename T>
-using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
-
-namespace android {
-namespace snapshot {
-
-// Intersect two linear extents. If no intersection, return an extent with length 0.
-static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
-    // Convert target_extent and existing_extent to linear extents. Zero extents
-    // doesn't matter and doesn't result in any intersection.
-    auto existing_linear_extent = existing_extent->AsLinearExtent();
-    if (!existing_linear_extent) return nullptr;
-
-    auto target_linear_extent = target_extent->AsLinearExtent();
-    if (!target_linear_extent) return nullptr;
-
-    return Interval::Intersect(target_linear_extent->AsInterval(),
-                               existing_linear_extent->AsInterval())
-            .AsExtent();
-}
-
-// Check that partition |p| contains |e| fully. Both of them should
-// be from |target_metadata|.
-// Returns true as long as |e| is a subrange of any extent of |p|.
-bool PartitionCowCreator::HasExtent(Partition* p, Extent* e) {
-    for (auto& partition_extent : p->extents()) {
-        auto intersection = Intersect(partition_extent.get(), e);
-        if (intersection != nullptr && intersection->num_sectors() == e->num_sectors()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-uint64_t PartitionCowCreator::GetCowSize() {
-    // WARNING: The origin partition should be READ-ONLY
-    const uint64_t logical_block_size = current_metadata->logical_block_size();
-    const unsigned int sectors_per_block = logical_block_size / kSectorSize;
-    DmSnapCowSizeCalculator sc(kSectorSize, kSnapshotChunkSize);
-
-    if (operations == nullptr) return sc.cow_size_bytes();
-
-    for (const auto& iop : *operations) {
-        for (const auto& de : iop.dst_extents()) {
-            // Skip if no blocks are written
-            if (de.num_blocks() == 0) continue;
-
-            // Flag all the blocks that were written
-            const auto block_boundary = de.start_block() + de.num_blocks();
-            for (auto b = de.start_block(); b < block_boundary; ++b) {
-                for (unsigned int s = 0; s < sectors_per_block; ++s) {
-                    const auto sector_id = b * sectors_per_block + s;
-                    sc.WriteSector(sector_id);
-                }
-            }
-        }
-    }
-
-    return sc.cow_size_bytes();
-}
-
-std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
-    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
-          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
-
-    const uint64_t logical_block_size = current_metadata->logical_block_size();
-    CHECK(logical_block_size != 0 && !(logical_block_size & (logical_block_size - 1)))
-            << "logical_block_size is not power of 2";
-
-    Return ret;
-    ret.snapshot_status.set_name(target_partition->name());
-    ret.snapshot_status.set_device_size(target_partition->size());
-    ret.snapshot_status.set_snapshot_size(target_partition->size());
-
-    // Being the COW partition virtual, its size doesn't affect the storage
-    // memory that will be occupied by the target.
-    // The actual storage space is affected by the COW file, whose size depends
-    // on the chunks that diverged between |current| and |target|.
-    // If the |target| partition is bigger than |current|, the data that is
-    // modified outside of |current| can be written directly to |current|.
-    // This because the data that will be written outside of |current| would
-    // not invalidate any useful information of |current|, thus:
-    // - if the snapshot is accepted for merge, this data would be already at
-    // the right place and should not be copied;
-    // - in the unfortunate case of the snapshot to be discarded, the regions
-    // modified by this data can be set as free regions and reused.
-    // Compute regions that are free in both current and target metadata. These are the regions
-    // we can use for COW partition.
-    auto target_free_regions = target_metadata->GetFreeRegions();
-    auto current_free_regions = current_metadata->GetFreeRegions();
-    auto free_regions = Interval::Intersect(target_free_regions, current_free_regions);
-    uint64_t free_region_length = 0;
-    for (const auto& interval : free_regions) {
-        free_region_length += interval.length();
-    }
-    free_region_length *= kSectorSize;
-
-    LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
-    auto cow_size = GetCowSize();
-
-    // Compute the COW partition size.
-    uint64_t cow_partition_size = std::min(cow_size, free_region_length);
-    // Round it down to the nearest logical block. Logical partitions must be a multiple
-    // of logical blocks.
-    cow_partition_size &= ~(logical_block_size - 1);
-    ret.snapshot_status.set_cow_partition_size(cow_partition_size);
-    // Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
-    ret.cow_partition_usable_regions = std::move(free_regions);
-
-    auto cow_file_size = cow_size - cow_partition_size;
-    // Round it up to the nearest sector.
-    cow_file_size += kSectorSize - 1;
-    cow_file_size &= ~(kSectorSize - 1);
-    ret.snapshot_status.set_cow_file_size(cow_file_size);
-
-    return ret;
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
deleted file mode 100644
index d3d186b..0000000
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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 <optional>
-#include <string>
-
-#include <liblp/builder.h>
-#include <update_engine/update_metadata.pb.h>
-
-#include <android/snapshot/snapshot.pb.h>
-
-namespace android {
-namespace snapshot {
-
-// Helper class that creates COW for a partition.
-struct PartitionCowCreator {
-    using Extent = android::fs_mgr::Extent;
-    using Interval = android::fs_mgr::Interval;
-    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
-    using Partition = android::fs_mgr::Partition;
-    using InstallOperation = chromeos_update_engine::InstallOperation;
-    template <typename T>
-    using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
-
-    // The metadata that will be written to target metadata slot.
-    MetadataBuilder* target_metadata = nullptr;
-    // The suffix of the target slot.
-    std::string target_suffix;
-    // The partition in target_metadata that needs to be snapshotted.
-    Partition* target_partition = nullptr;
-    // The metadata at the current slot (that would be used if the device boots
-    // normally). This is used to determine which extents are being used.
-    MetadataBuilder* current_metadata = nullptr;
-    // The suffix of the current slot.
-    std::string current_suffix;
-    // List of operations to be applied on the partition.
-    const RepeatedPtrField<InstallOperation>* operations = nullptr;
-
-    struct Return {
-        SnapshotStatus snapshot_status;
-        std::vector<Interval> cow_partition_usable_regions;
-    };
-
-    std::optional<Return> Run();
-
-  private:
-    bool HasExtent(Partition* p, Extent* e);
-    uint64_t GetCowSize();
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
deleted file mode 100644
index eae6c35..0000000
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ /dev/null
@@ -1,216 +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 <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-#include <liblp/builder.h>
-#include <liblp/property_fetcher.h>
-
-#include "dm_snapshot_internals.h"
-#include "partition_cow_creator.h"
-#include "test_helpers.h"
-#include "utility.h"
-
-using namespace android::fs_mgr;
-
-namespace android {
-namespace snapshot {
-
-class PartitionCowCreatorTest : public ::testing::Test {
-  public:
-    void SetUp() override { SnapshotTestPropertyFetcher::SetUp(); }
-    void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
-};
-
-TEST_F(PartitionCowCreatorTest, IntersectSelf) {
-    constexpr uint64_t initial_size = 1_MiB;
-    constexpr uint64_t final_size = 40_KiB;
-
-    auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
-    ASSERT_NE(builder_a, nullptr);
-    auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
-    ASSERT_NE(system_a, nullptr);
-    ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
-
-    auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
-    ASSERT_NE(builder_b, nullptr);
-    auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
-    ASSERT_NE(system_b, nullptr);
-    ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
-
-    PartitionCowCreator creator{.target_metadata = builder_b.get(),
-                                .target_suffix = "_b",
-                                .target_partition = system_b,
-                                .current_metadata = builder_a.get(),
-                                .current_suffix = "_a"};
-    auto ret = creator.Run();
-    ASSERT_TRUE(ret.has_value());
-    ASSERT_EQ(final_size, ret->snapshot_status.device_size());
-    ASSERT_EQ(final_size, ret->snapshot_status.snapshot_size());
-}
-
-TEST_F(PartitionCowCreatorTest, Holes) {
-    const auto& opener = test_device->GetPartitionOpener();
-
-    constexpr auto slack_space = 1_MiB;
-    constexpr auto big_size = (kSuperSize - slack_space) / 2;
-    constexpr auto small_size = big_size / 2;
-
-    BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4_KiB);
-    std::vector<BlockDeviceInfo> devices = {super_device};
-    auto source = MetadataBuilder::New(devices, "super", 1_KiB, 2);
-    auto system = source->AddPartition("system_a", 0);
-    ASSERT_NE(nullptr, system);
-    ASSERT_TRUE(source->ResizePartition(system, big_size));
-    auto vendor = source->AddPartition("vendor_a", 0);
-    ASSERT_NE(nullptr, vendor);
-    ASSERT_TRUE(source->ResizePartition(vendor, big_size));
-    // Create a hole between system and vendor
-    ASSERT_TRUE(source->ResizePartition(system, small_size));
-    auto source_metadata = source->Export();
-    ASSERT_NE(nullptr, source_metadata);
-    ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *source_metadata.get()));
-
-    auto target = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
-    // Shrink vendor
-    vendor = target->FindPartition("vendor_b");
-    ASSERT_NE(nullptr, vendor);
-    ASSERT_TRUE(target->ResizePartition(vendor, small_size));
-    // Grow system to take hole & saved space from vendor
-    system = target->FindPartition("system_b");
-    ASSERT_NE(nullptr, system);
-    ASSERT_TRUE(target->ResizePartition(system, big_size * 2 - small_size));
-
-    PartitionCowCreator creator{.target_metadata = target.get(),
-                                .target_suffix = "_b",
-                                .target_partition = system,
-                                .current_metadata = source.get(),
-                                .current_suffix = "_a"};
-    auto ret = creator.Run();
-    ASSERT_TRUE(ret.has_value());
-}
-
-TEST_F(PartitionCowCreatorTest, CowSize) {
-    using InstallOperation = chromeos_update_engine::InstallOperation;
-    using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;
-    using Extent = chromeos_update_engine::Extent;
-
-    constexpr uint64_t initial_size = 50_MiB;
-    constexpr uint64_t final_size = 40_MiB;
-
-    auto builder_a = MetadataBuilder::New(initial_size, 1_KiB, 2);
-    ASSERT_NE(builder_a, nullptr);
-    auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
-    ASSERT_NE(system_a, nullptr);
-    ASSERT_TRUE(builder_a->ResizePartition(system_a, final_size));
-
-    auto builder_b = MetadataBuilder::New(initial_size, 1_KiB, 2);
-    ASSERT_NE(builder_b, nullptr);
-    auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
-    ASSERT_NE(system_b, nullptr);
-    ASSERT_TRUE(builder_b->ResizePartition(system_b, final_size));
-
-    const uint64_t block_size = builder_b->logical_block_size();
-    const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;
-    ASSERT_EQ(chunk_size, block_size);
-
-    auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a,
-                              MetadataBuilder* builder_b, Partition* system_b) {
-        RepeatedInstallOperationPtr riop(iopv.begin(), iopv.end());
-        PartitionCowCreator creator{.target_metadata = builder_b,
-                                    .target_suffix = "_b",
-                                    .target_partition = system_b,
-                                    .current_metadata = builder_a,
-                                    .current_suffix = "_a",
-                                    .operations = &riop};
-
-        auto ret = creator.Run();
-
-        if (ret.has_value()) {
-            return ret->snapshot_status.cow_file_size() + ret->snapshot_status.cow_partition_size();
-        }
-        return std::numeric_limits<uint64_t>::max();
-    };
-
-    std::vector<InstallOperation> iopv;
-    InstallOperation iop;
-    Extent* e;
-
-    // No data written, no operations performed
-    ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
-
-    // No data written
-    e = iop.add_dst_extents();
-    e->set_start_block(0);
-    e->set_num_blocks(0);
-    iopv.push_back(iop);
-    ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
-
-    e = iop.add_dst_extents();
-    e->set_start_block(1);
-    e->set_num_blocks(0);
-    iopv.push_back(iop);
-    ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
-
-    // Fill the first block
-    e = iop.add_dst_extents();
-    e->set_start_block(0);
-    e->set_num_blocks(1);
-    iopv.push_back(iop);
-    ASSERT_EQ(3 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
-
-    // Fill the second block
-    e = iop.add_dst_extents();
-    e->set_start_block(1);
-    e->set_num_blocks(1);
-    iopv.push_back(iop);
-    ASSERT_EQ(4 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
-
-    // Jump to 5th block and write 2
-    e = iop.add_dst_extents();
-    e->set_start_block(5);
-    e->set_num_blocks(2);
-    iopv.push_back(iop);
-    ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));
-}
-
-TEST(DmSnapshotInternals, CowSizeCalculator) {
-    DmSnapCowSizeCalculator cc(512, 8);
-    unsigned long int b;
-
-    // Empty COW
-    ASSERT_EQ(cc.cow_size_sectors(), 16);
-
-    // First chunk written
-    for (b = 0; b < 4_KiB; ++b) {
-        cc.WriteByte(b);
-        ASSERT_EQ(cc.cow_size_sectors(), 24);
-    }
-
-    // Second chunk written
-    for (b = 4_KiB; b < 8_KiB; ++b) {
-        cc.WriteByte(b);
-        ASSERT_EQ(cc.cow_size_sectors(), 32);
-    }
-
-    // Leave a hole and write 5th chunk
-    for (b = 16_KiB; b < 20_KiB; ++b) {
-        cc.WriteByte(b);
-        ASSERT_EQ(cc.cow_size_sectors(), 40);
-    }
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
deleted file mode 100644
index 2c516a2..0000000
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ /dev/null
@@ -1,2196 +0,0 @@
-// 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 <libsnapshot/snapshot.h>
-
-#include <dirent.h>
-#include <math.h>
-#include <sys/file.h>
-#include <sys/types.h>
-#include <sys/unistd.h>
-
-#include <optional>
-#include <thread>
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#ifdef LIBSNAPSHOT_USE_HAL
-#include <android/hardware/boot/1.1/IBootControl.h>
-#endif
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr.h>
-#include <fs_mgr_dm_linear.h>
-#include <fs_mgr_overlayfs.h>
-#include <fstab/fstab.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/liblp.h>
-
-#include <android/snapshot/snapshot.pb.h>
-#include "partition_cow_creator.h"
-#include "snapshot_metadata_updater.h"
-#include "utility.h"
-
-namespace android {
-namespace snapshot {
-
-using android::base::unique_fd;
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::dm::DmTable;
-using android::dm::DmTargetLinear;
-using android::dm::DmTargetSnapshot;
-using android::dm::kSectorSize;
-using android::dm::SnapshotStorageMode;
-using android::fiemap::IImageManager;
-using android::fs_mgr::CreateDmTable;
-using android::fs_mgr::CreateLogicalPartition;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fs_mgr::GetPartitionGroupName;
-using android::fs_mgr::GetPartitionName;
-using android::fs_mgr::LpMetadata;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::SlotNumberForSlotSuffix;
-using android::hardware::boot::V1_1::MergeStatus;
-using chromeos_update_engine::DeltaArchiveManifest;
-using chromeos_update_engine::InstallOperation;
-template <typename T>
-using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
-using std::chrono::duration_cast;
-using namespace std::chrono_literals;
-using namespace std::string_literals;
-
-static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
-
-#ifdef __ANDROID_RECOVERY__
-constexpr bool kIsRecovery = true;
-#else
-constexpr bool kIsRecovery = false;
-#endif
-
-class DeviceInfo final : public SnapshotManager::IDeviceInfo {
-  public:
-    std::string GetGsidDir() const override { return "ota"s; }
-    std::string GetMetadataDir() const override { return "/metadata/ota"s; }
-    std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
-    std::string GetOtherSlotSuffix() const override { return fs_mgr_get_other_slot_suffix(); }
-    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const { return opener_; }
-    std::string GetSuperDevice(uint32_t slot) const override {
-        return fs_mgr_get_super_partition_name(slot);
-    }
-    bool IsOverlayfsSetup() const override { return fs_mgr_overlayfs_is_setup(); }
-    bool SetBootControlMergeStatus(MergeStatus status) override;
-    bool IsRecovery() const override { return kIsRecovery; }
-
-  private:
-    android::fs_mgr::PartitionOpener opener_;
-#ifdef LIBSNAPSHOT_USE_HAL
-    android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
-#endif
-};
-
-bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {
-#ifdef LIBSNAPSHOT_USE_HAL
-    if (!boot_control_) {
-        auto hal = android::hardware::boot::V1_0::IBootControl::getService();
-        if (!hal) {
-            LOG(ERROR) << "Could not find IBootControl HAL";
-            return false;
-        }
-        boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
-        if (!boot_control_) {
-            LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
-            return false;
-        }
-    }
-    if (!boot_control_->setSnapshotMergeStatus(status)) {
-        LOG(ERROR) << "Unable to set the snapshot merge status";
-        return false;
-    }
-    return true;
-#else
-    return false;
-#endif
-}
-
-// Note: IImageManager is an incomplete type in the header, so the default
-// destructor doesn't work.
-SnapshotManager::~SnapshotManager() {}
-
-std::unique_ptr<SnapshotManager> SnapshotManager::New(IDeviceInfo* info) {
-    if (!info) {
-        info = new DeviceInfo();
-    }
-    return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
-}
-
-std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
-    auto sm = New(info);
-    if (!sm || !sm->ForceLocalImageManager()) {
-        return nullptr;
-    }
-    return sm;
-}
-
-SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
-    gsid_dir_ = device_->GetGsidDir();
-    metadata_dir_ = device_->GetMetadataDir();
-}
-
-static std::string GetCowName(const std::string& snapshot_name) {
-    return snapshot_name + "-cow";
-}
-
-static std::string GetCowImageDeviceName(const std::string& snapshot_name) {
-    return snapshot_name + "-cow-img";
-}
-
-static std::string GetBaseDeviceName(const std::string& partition_name) {
-    return partition_name + "-base";
-}
-
-static std::string GetSnapshotExtraDeviceName(const std::string& snapshot_name) {
-    return snapshot_name + "-inner";
-}
-
-bool SnapshotManager::BeginUpdate() {
-    bool needs_merge = false;
-    if (!TryCancelUpdate(&needs_merge)) {
-        return false;
-    }
-    if (needs_merge) {
-        LOG(INFO) << "Wait for merge (if any) before beginning a new update.";
-        auto state = ProcessUpdateState();
-        LOG(INFO) << "Merged with state = " << state;
-    }
-
-    auto file = LockExclusive();
-    if (!file) return false;
-
-    auto state = ReadUpdateState(file.get());
-    if (state != UpdateState::None) {
-        LOG(ERROR) << "An update is already in progress, cannot begin a new update";
-        return false;
-    }
-    return WriteUpdateState(file.get(), UpdateState::Initiated);
-}
-
-bool SnapshotManager::CancelUpdate() {
-    bool needs_merge = false;
-    if (!TryCancelUpdate(&needs_merge)) {
-        return false;
-    }
-    if (needs_merge) {
-        LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
-    }
-    return !needs_merge;
-}
-
-bool SnapshotManager::TryCancelUpdate(bool* needs_merge) {
-    *needs_merge = false;
-
-    auto file = LockExclusive();
-    if (!file) return false;
-
-    UpdateState state = ReadUpdateState(file.get());
-    if (state == UpdateState::None) return true;
-
-    if (state == UpdateState::Initiated) {
-        LOG(INFO) << "Update has been initiated, now canceling";
-        return RemoveAllUpdateState(file.get());
-    }
-
-    if (state == UpdateState::Unverified) {
-        // We completed an update, but it can still be canceled if we haven't booted into it.
-        auto boot_file = GetSnapshotBootIndicatorPath();
-        std::string contents;
-        if (!android::base::ReadFileToString(boot_file, &contents)) {
-            PLOG(WARNING) << "Cannot read " << boot_file << ", proceed to canceling the update:";
-            return RemoveAllUpdateState(file.get());
-        }
-        if (device_->GetSlotSuffix() == contents) {
-            LOG(INFO) << "Canceling a previously completed update";
-            return RemoveAllUpdateState(file.get());
-        }
-    }
-    *needs_merge = true;
-    return true;
-}
-
-bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
-    if (!RemoveAllSnapshots(lock)) {
-        LOG(ERROR) << "Could not remove all snapshots";
-        return false;
-    }
-
-    RemoveSnapshotBootIndicator();
-
-    // If this fails, we'll keep trying to remove the update state (as the
-    // device reboots or starts a new update) until it finally succeeds.
-    return WriteUpdateState(lock, UpdateState::None);
-}
-
-bool SnapshotManager::FinishedSnapshotWrites() {
-    auto lock = LockExclusive();
-    if (!lock) return false;
-
-    auto update_state = ReadUpdateState(lock.get());
-    if (update_state == UpdateState::Unverified) {
-        LOG(INFO) << "FinishedSnapshotWrites already called before. Ignored.";
-        return true;
-    }
-
-    if (update_state != UpdateState::Initiated) {
-        LOG(ERROR) << "Can only transition to the Unverified state from the Initiated state.";
-        return false;
-    }
-
-    // This file acts as both a quick indicator for init (it can use access(2)
-    // to decide how to do first-stage mounts), and it stores the old slot, so
-    // we can tell whether or not we performed a rollback.
-    auto contents = device_->GetSlotSuffix();
-    auto boot_file = GetSnapshotBootIndicatorPath();
-    if (!android::base::WriteStringToFile(contents, boot_file)) {
-        PLOG(ERROR) << "write failed: " << boot_file;
-        return false;
-    }
-    return WriteUpdateState(lock.get(), UpdateState::Unverified);
-}
-
-bool SnapshotManager::CreateSnapshot(LockedFile* lock, SnapshotStatus* status) {
-    CHECK(lock);
-    CHECK(lock->lock_mode() == LOCK_EX);
-    CHECK(status);
-
-    if (status->name().empty()) {
-        LOG(ERROR) << "SnapshotStatus has no name.";
-        return false;
-    }
-    // Sanity check these sizes. Like liblp, we guarantee the partition size
-    // is respected, which means it has to be sector-aligned. (This guarantee
-    // is useful for locating avb footers correctly). The COW file size, however,
-    // can be arbitrarily larger than specified, so we can safely round it up.
-    if (status->device_size() % kSectorSize != 0) {
-        LOG(ERROR) << "Snapshot " << status->name()
-                   << " device size is not a multiple of the sector size: "
-                   << status->device_size();
-        return false;
-    }
-    if (status->snapshot_size() % kSectorSize != 0) {
-        LOG(ERROR) << "Snapshot " << status->name()
-                   << " snapshot size is not a multiple of the sector size: "
-                   << status->snapshot_size();
-        return false;
-    }
-    if (status->cow_partition_size() % kSectorSize != 0) {
-        LOG(ERROR) << "Snapshot " << status->name()
-                   << " cow partition size is not a multiple of the sector size: "
-                   << status->cow_partition_size();
-        return false;
-    }
-    if (status->cow_file_size() % kSectorSize != 0) {
-        LOG(ERROR) << "Snapshot " << status->name()
-                   << " cow file size is not a multiple of the sector size: "
-                   << status->cow_file_size();
-        return false;
-    }
-
-    status->set_state(SnapshotState::CREATED);
-    status->set_sectors_allocated(0);
-    status->set_metadata_sectors(0);
-
-    if (!WriteSnapshotStatus(lock, *status)) {
-        PLOG(ERROR) << "Could not write snapshot status: " << status->name();
-        return false;
-    }
-    return true;
-}
-
-bool SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {
-    CHECK(lock);
-    CHECK(lock->lock_mode() == LOCK_EX);
-    if (!EnsureImageManager()) return false;
-
-    SnapshotStatus status;
-    if (!ReadSnapshotStatus(lock, name, &status)) {
-        return false;
-    }
-
-    // The COW file size should have been rounded up to the nearest sector in CreateSnapshot.
-    // Sanity check this.
-    if (status.cow_file_size() % kSectorSize != 0) {
-        LOG(ERROR) << "Snapshot " << name << " COW file size is not a multiple of the sector size: "
-                   << status.cow_file_size();
-        return false;
-    }
-
-    std::string cow_image_name = GetCowImageDeviceName(name);
-    int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
-    return images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags);
-}
-
-bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
-                                  const std::string& base_device, const std::string& cow_device,
-                                  const std::chrono::milliseconds& timeout_ms,
-                                  std::string* dev_path) {
-    CHECK(lock);
-
-    SnapshotStatus status;
-    if (!ReadSnapshotStatus(lock, name, &status)) {
-        return false;
-    }
-    if (status.state() == SnapshotState::MERGE_COMPLETED) {
-        LOG(ERROR) << "Should not create a snapshot device for " << name
-                   << " after merging has completed.";
-        return false;
-    }
-
-    // Validate the block device size, as well as the requested snapshot size.
-    // Note that during first-stage init, we don't have the device paths.
-    if (android::base::StartsWith(base_device, "/")) {
-        unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
-        if (fd < 0) {
-            PLOG(ERROR) << "open failed: " << base_device;
-            return false;
-        }
-        auto dev_size = get_block_device_size(fd);
-        if (!dev_size) {
-            PLOG(ERROR) << "Could not determine block device size: " << base_device;
-            return false;
-        }
-        if (status.device_size() != dev_size) {
-            LOG(ERROR) << "Block device size for " << base_device << " does not match"
-                       << "(expected " << status.device_size() << ", got " << dev_size << ")";
-            return false;
-        }
-    }
-    if (status.device_size() % kSectorSize != 0) {
-        LOG(ERROR) << "invalid blockdev size for " << base_device << ": " << status.device_size();
-        return false;
-    }
-    if (status.snapshot_size() % kSectorSize != 0 ||
-        status.snapshot_size() > status.device_size()) {
-        LOG(ERROR) << "Invalid snapshot size for " << base_device << ": " << status.snapshot_size();
-        return false;
-    }
-    uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
-    uint64_t linear_sectors = (status.device_size() - status.snapshot_size()) / kSectorSize;
-
-    auto& dm = DeviceMapper::Instance();
-
-    // Note that merging is a global state. We do track whether individual devices
-    // have completed merging, but the start of the merge process is considered
-    // atomic.
-    SnapshotStorageMode mode;
-    switch (ReadUpdateState(lock)) {
-        case UpdateState::MergeCompleted:
-        case UpdateState::MergeNeedsReboot:
-            LOG(ERROR) << "Should not create a snapshot device for " << name
-                       << " after global merging has completed.";
-            return false;
-        case UpdateState::Merging:
-        case UpdateState::MergeFailed:
-            // Note: MergeFailed indicates that a merge is in progress, but
-            // is possibly stalled. We still have to honor the merge.
-            mode = SnapshotStorageMode::Merge;
-            break;
-        default:
-            mode = SnapshotStorageMode::Persistent;
-            break;
-    }
-
-    // The kernel (tested on 4.19) crashes horribly if a device has both a snapshot
-    // and a linear target in the same table. Instead, we stack them, and give the
-    // snapshot device a different name. It is not exposed to the caller in this
-    // case.
-    auto snap_name = (linear_sectors > 0) ? GetSnapshotExtraDeviceName(name) : name;
-
-    DmTable table;
-    table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,
-                                    kSnapshotChunkSize);
-    if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
-        LOG(ERROR) << "Could not create snapshot device: " << snap_name;
-        return false;
-    }
-
-    if (linear_sectors) {
-        std::string snap_dev;
-        if (!dm.GetDeviceString(snap_name, &snap_dev)) {
-            LOG(ERROR) << "Cannot determine major/minor for: " << snap_name;
-            return false;
-        }
-
-        // Our stacking will looks like this:
-        //     [linear, linear] ; to snapshot, and non-snapshot region of base device
-        //     [snapshot-inner]
-        //     [base device]   [cow]
-        DmTable table;
-        table.Emplace<DmTargetLinear>(0, snapshot_sectors, snap_dev, 0);
-        table.Emplace<DmTargetLinear>(snapshot_sectors, linear_sectors, base_device,
-                                      snapshot_sectors);
-        if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
-            LOG(ERROR) << "Could not create outer snapshot device: " << name;
-            dm.DeleteDevice(snap_name);
-            return false;
-        }
-    }
-
-    // :TODO: when merging is implemented, we need to add an argument to the
-    // status indicating how much progress is left to merge. (device-mapper
-    // does not retain the initial values, so we can't derive them.)
-    return true;
-}
-
-std::optional<std::string> SnapshotManager::MapCowImage(
-        const std::string& name, const std::chrono::milliseconds& timeout_ms) {
-    if (!EnsureImageManager()) return std::nullopt;
-    auto cow_image_name = GetCowImageDeviceName(name);
-
-    bool ok;
-    std::string cow_dev;
-    if (has_local_image_manager_) {
-        // If we forced a local image manager, it means we don't have binder,
-        // which means first-stage init. We must use device-mapper.
-        const auto& opener = device_->GetPartitionOpener();
-        ok = images_->MapImageWithDeviceMapper(opener, cow_image_name, &cow_dev);
-    } else {
-        ok = images_->MapImageDevice(cow_image_name, timeout_ms, &cow_dev);
-    }
-
-    if (ok) {
-        LOG(INFO) << "Mapped " << cow_image_name << " to " << cow_dev;
-        return cow_dev;
-    }
-    LOG(ERROR) << "Could not map image device: " << cow_image_name;
-    return std::nullopt;
-}
-
-bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
-    CHECK(lock);
-
-    auto& dm = DeviceMapper::Instance();
-    if (!dm.DeleteDeviceIfExists(name)) {
-        LOG(ERROR) << "Could not delete snapshot device: " << name;
-        return false;
-    }
-
-    auto snapshot_extra_device = GetSnapshotExtraDeviceName(name);
-    if (!dm.DeleteDeviceIfExists(snapshot_extra_device)) {
-        LOG(ERROR) << "Could not delete snapshot inner device: " << snapshot_extra_device;
-        return false;
-    }
-
-    return true;
-}
-
-bool SnapshotManager::UnmapCowImage(const std::string& name) {
-    if (!EnsureImageManager()) return false;
-    return images_->UnmapImageIfExists(GetCowImageDeviceName(name));
-}
-
-bool SnapshotManager::DeleteSnapshot(LockedFile* lock, const std::string& name) {
-    CHECK(lock);
-    CHECK(lock->lock_mode() == LOCK_EX);
-    if (!EnsureImageManager()) return false;
-
-    if (!UnmapCowDevices(lock, name)) {
-        return false;
-    }
-
-    auto cow_image_name = GetCowImageDeviceName(name);
-    if (images_->BackingImageExists(cow_image_name)) {
-        if (!images_->DeleteBackingImage(cow_image_name)) {
-            return false;
-        }
-    }
-
-    std::string error;
-    auto file_path = GetSnapshotStatusFilePath(name);
-    if (!android::base::RemoveFileIfExists(file_path, &error)) {
-        LOG(ERROR) << "Failed to remove status file " << file_path << ": " << error;
-        return false;
-    }
-    return true;
-}
-
-bool SnapshotManager::InitiateMerge() {
-    auto lock = LockExclusive();
-    if (!lock) return false;
-
-    UpdateState state = ReadUpdateState(lock.get());
-    if (state != UpdateState::Unverified) {
-        LOG(ERROR) << "Cannot begin a merge if an update has not been verified";
-        return false;
-    }
-
-    std::string old_slot;
-    auto boot_file = GetSnapshotBootIndicatorPath();
-    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
-        LOG(ERROR) << "Could not determine the previous slot; aborting merge";
-        return false;
-    }
-    auto new_slot = device_->GetSlotSuffix();
-    if (new_slot == old_slot) {
-        LOG(ERROR) << "Device cannot merge while booting off old slot " << old_slot;
-        return false;
-    }
-
-    std::vector<std::string> snapshots;
-    if (!ListSnapshots(lock.get(), &snapshots)) {
-        LOG(ERROR) << "Could not list snapshots";
-        return false;
-    }
-
-    auto& dm = DeviceMapper::Instance();
-    for (const auto& snapshot : snapshots) {
-        // The device has to be mapped, since everything should be merged at
-        // the same time. This is a fairly serious error. We could forcefully
-        // map everything here, but it should have been mapped during first-
-        // stage init.
-        if (dm.GetState(snapshot) == DmDeviceState::INVALID) {
-            LOG(ERROR) << "Cannot begin merge; device " << snapshot << " is not mapped.";
-            return false;
-        }
-    }
-
-    auto metadata = ReadCurrentMetadata();
-    for (auto it = snapshots.begin(); it != snapshots.end();) {
-        switch (GetMetadataPartitionState(*metadata, *it)) {
-            case MetadataPartitionState::Flashed:
-                LOG(WARNING) << "Detected re-flashing for partition " << *it
-                             << ". Skip merging it.";
-                [[fallthrough]];
-            case MetadataPartitionState::None: {
-                LOG(WARNING) << "Deleting snapshot for partition " << *it;
-                if (!DeleteSnapshot(lock.get(), *it)) {
-                    LOG(WARNING) << "Cannot delete snapshot for partition " << *it
-                                 << ". Skip merging it anyways.";
-                }
-                it = snapshots.erase(it);
-            } break;
-            case MetadataPartitionState::Updated: {
-                ++it;
-            } break;
-        }
-    }
-
-    // Point of no return - mark that we're starting a merge. From now on every
-    // snapshot must be a merge target.
-    if (!WriteUpdateState(lock.get(), UpdateState::Merging)) {
-        return false;
-    }
-
-    bool rewrote_all = true;
-    for (const auto& snapshot : snapshots) {
-        // If this fails, we have no choice but to continue. Everything must
-        // be merged. This is not an ideal state to be in, but it is safe,
-        // because we the next boot will try again.
-        if (!SwitchSnapshotToMerge(lock.get(), snapshot)) {
-            LOG(ERROR) << "Failed to switch snapshot to a merge target: " << snapshot;
-            rewrote_all = false;
-        }
-    }
-
-    // If we couldn't switch everything to a merge target, pre-emptively mark
-    // this merge as failed. It will get acknowledged when WaitForMerge() is
-    // called.
-    if (!rewrote_all) {
-        WriteUpdateState(lock.get(), UpdateState::MergeFailed);
-    }
-
-    // Return true no matter what, because a merge was initiated.
-    return true;
-}
-
-bool SnapshotManager::SwitchSnapshotToMerge(LockedFile* lock, const std::string& name) {
-    SnapshotStatus status;
-    if (!ReadSnapshotStatus(lock, name, &status)) {
-        return false;
-    }
-    if (status.state() != SnapshotState::CREATED) {
-        LOG(WARNING) << "Snapshot " << name
-                     << " has unexpected state: " << SnapshotState_Name(status.state());
-    }
-
-    // After this, we return true because we technically did switch to a merge
-    // target. Everything else we do here is just informational.
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    if (!RewriteSnapshotDeviceTable(dm_name)) {
-        return false;
-    }
-
-    status.set_state(SnapshotState::MERGING);
-
-    DmTargetSnapshot::Status dm_status;
-    if (!QuerySnapshotStatus(dm_name, nullptr, &dm_status)) {
-        LOG(ERROR) << "Could not query merge status for snapshot: " << dm_name;
-    }
-    status.set_sectors_allocated(dm_status.sectors_allocated);
-    status.set_metadata_sectors(dm_status.metadata_sectors);
-    if (!WriteSnapshotStatus(lock, status)) {
-        LOG(ERROR) << "Could not update status file for snapshot: " << name;
-    }
-    return true;
-}
-
-bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& dm_name) {
-    auto& dm = DeviceMapper::Instance();
-
-    std::vector<DeviceMapper::TargetInfo> old_targets;
-    if (!dm.GetTableInfo(dm_name, &old_targets)) {
-        LOG(ERROR) << "Could not read snapshot device table: " << dm_name;
-        return false;
-    }
-    if (old_targets.size() != 1 || DeviceMapper::GetTargetType(old_targets[0].spec) != "snapshot") {
-        LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << dm_name;
-        return false;
-    }
-
-    std::string base_device, cow_device;
-    if (!DmTargetSnapshot::GetDevicesFromParams(old_targets[0].data, &base_device, &cow_device)) {
-        LOG(ERROR) << "Could not derive underlying devices for snapshot: " << dm_name;
-        return false;
-    }
-
-    DmTable table;
-    table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
-                                    SnapshotStorageMode::Merge, kSnapshotChunkSize);
-    if (!dm.LoadTableAndActivate(dm_name, table)) {
-        LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << dm_name;
-        return false;
-    }
-    LOG(INFO) << "Successfully switched snapshot device to a merge target: " << dm_name;
-    return true;
-}
-
-enum class TableQuery {
-    Table,
-    Status,
-};
-
-static bool GetSingleTarget(const std::string& dm_name, TableQuery query,
-                            DeviceMapper::TargetInfo* target) {
-    auto& dm = DeviceMapper::Instance();
-    if (dm.GetState(dm_name) == DmDeviceState::INVALID) {
-        return false;
-    }
-
-    std::vector<DeviceMapper::TargetInfo> targets;
-    bool result;
-    if (query == TableQuery::Status) {
-        result = dm.GetTableStatus(dm_name, &targets);
-    } else {
-        result = dm.GetTableInfo(dm_name, &targets);
-    }
-    if (!result) {
-        LOG(ERROR) << "Could not query device: " << dm_name;
-        return false;
-    }
-    if (targets.size() != 1) {
-        return false;
-    }
-
-    *target = std::move(targets[0]);
-    return true;
-}
-
-bool SnapshotManager::IsSnapshotDevice(const std::string& dm_name, TargetInfo* target) {
-    DeviceMapper::TargetInfo snap_target;
-    if (!GetSingleTarget(dm_name, TableQuery::Status, &snap_target)) {
-        return false;
-    }
-    auto type = DeviceMapper::GetTargetType(snap_target.spec);
-    if (type != "snapshot" && type != "snapshot-merge") {
-        return false;
-    }
-    if (target) {
-        *target = std::move(snap_target);
-    }
-    return true;
-}
-
-bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,
-                                          DmTargetSnapshot::Status* status) {
-    DeviceMapper::TargetInfo target;
-    if (!IsSnapshotDevice(dm_name, &target)) {
-        LOG(ERROR) << "Device " << dm_name << " is not a snapshot or snapshot-merge device";
-        return false;
-    }
-    if (!DmTargetSnapshot::ParseStatusText(target.data, status)) {
-        LOG(ERROR) << "Could not parse snapshot status text: " << dm_name;
-        return false;
-    }
-    if (target_type) {
-        *target_type = DeviceMapper::GetTargetType(target.spec);
-    }
-    return true;
-}
-
-// Note that when a merge fails, we will *always* try again to complete the
-// merge each time the device boots. There is no harm in doing so, and if
-// the problem was transient, we might manage to get a new outcome.
-UpdateState SnapshotManager::ProcessUpdateState() {
-    while (true) {
-        UpdateState state = CheckMergeState();
-        if (state == UpdateState::MergeFailed) {
-            AcknowledgeMergeFailure();
-        }
-        if (state != UpdateState::Merging) {
-            // Either there is no merge, or the merge was finished, so no need
-            // to keep waiting.
-            return state;
-        }
-
-        // This wait is not super time sensitive, so we have a relatively
-        // low polling frequency.
-        std::this_thread::sleep_for(2s);
-    }
-}
-
-UpdateState SnapshotManager::CheckMergeState() {
-    auto lock = LockExclusive();
-    if (!lock) {
-        return UpdateState::MergeFailed;
-    }
-
-    UpdateState state = CheckMergeState(lock.get());
-    if (state == UpdateState::MergeCompleted) {
-        // Do this inside the same lock. Failures get acknowledged without the
-        // lock, because flock() might have failed.
-        AcknowledgeMergeSuccess(lock.get());
-    } else if (state == UpdateState::Cancelled) {
-        RemoveAllUpdateState(lock.get());
-    }
-    return state;
-}
-
-UpdateState SnapshotManager::CheckMergeState(LockedFile* lock) {
-    UpdateState state = ReadUpdateState(lock);
-    switch (state) {
-        case UpdateState::None:
-        case UpdateState::MergeCompleted:
-            // Harmless races are allowed between two callers of WaitForMerge,
-            // so in both of these cases we just propagate the state.
-            return state;
-
-        case UpdateState::Merging:
-        case UpdateState::MergeNeedsReboot:
-        case UpdateState::MergeFailed:
-            // We'll poll each snapshot below. Note that for the NeedsReboot
-            // case, we always poll once to give cleanup another opportunity to
-            // run.
-            break;
-
-        case UpdateState::Unverified:
-            // This is an edge case. Normally cancelled updates are detected
-            // via the merge poll below, but if we never started a merge, we
-            // need to also check here.
-            if (HandleCancelledUpdate(lock)) {
-                return UpdateState::Cancelled;
-            }
-            return state;
-
-        default:
-            return state;
-    }
-
-    std::vector<std::string> snapshots;
-    if (!ListSnapshots(lock, &snapshots)) {
-        return UpdateState::MergeFailed;
-    }
-
-    bool cancelled = false;
-    bool failed = false;
-    bool merging = false;
-    bool needs_reboot = false;
-    for (const auto& snapshot : snapshots) {
-        UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot);
-        switch (snapshot_state) {
-            case UpdateState::MergeFailed:
-                failed = true;
-                break;
-            case UpdateState::Merging:
-                merging = true;
-                break;
-            case UpdateState::MergeNeedsReboot:
-                needs_reboot = true;
-                break;
-            case UpdateState::MergeCompleted:
-                break;
-            case UpdateState::Cancelled:
-                cancelled = true;
-                break;
-            default:
-                LOG(ERROR) << "Unknown merge status: " << static_cast<uint32_t>(snapshot_state);
-                failed = true;
-                break;
-        }
-    }
-
-    if (merging) {
-        // Note that we handle "Merging" before we handle anything else. We
-        // want to poll until *nothing* is merging if we can, so everything has
-        // a chance to get marked as completed or failed.
-        return UpdateState::Merging;
-    }
-    if (failed) {
-        // Note: since there are many drop-out cases for failure, we acknowledge
-        // it in WaitForMerge rather than here and elsewhere.
-        return UpdateState::MergeFailed;
-    }
-    if (needs_reboot) {
-        WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
-        return UpdateState::MergeNeedsReboot;
-    }
-    if (cancelled) {
-        // This is an edge case, that we handle as correctly as we sensibly can.
-        // The underlying partition has changed behind update_engine, and we've
-        // removed the snapshot as a result. The exact state of the update is
-        // undefined now, but this can only happen on an unlocked device where
-        // partitions can be flashed without wiping userdata.
-        return UpdateState::Cancelled;
-    }
-    return UpdateState::MergeCompleted;
-}
-
-UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name) {
-    SnapshotStatus snapshot_status;
-    if (!ReadSnapshotStatus(lock, name, &snapshot_status)) {
-        return UpdateState::MergeFailed;
-    }
-
-    std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
-
-    std::unique_ptr<LpMetadata> current_metadata;
-
-    if (!IsSnapshotDevice(dm_name)) {
-        if (!current_metadata) {
-            current_metadata = ReadCurrentMetadata();
-        }
-
-        if (!current_metadata ||
-            GetMetadataPartitionState(*current_metadata, name) != MetadataPartitionState::Updated) {
-            DeleteSnapshot(lock, name);
-            return UpdateState::Cancelled;
-        }
-
-        // During a check, we decided the merge was complete, but we were unable to
-        // collapse the device-mapper stack and perform COW cleanup. If we haven't
-        // rebooted after this check, the device will still be a snapshot-merge
-        // target. If the have rebooted, the device will now be a linear target,
-        // and we can try cleanup again.
-        if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
-            // NB: It's okay if this fails now, we gave cleanup our best effort.
-            OnSnapshotMergeComplete(lock, name, snapshot_status);
-            return UpdateState::MergeCompleted;
-        }
-
-        LOG(ERROR) << "Expected snapshot or snapshot-merge for device: " << dm_name;
-        return UpdateState::MergeFailed;
-    }
-
-    // This check is expensive so it is only enabled for debugging.
-    DCHECK((current_metadata = ReadCurrentMetadata()) &&
-           GetMetadataPartitionState(*current_metadata, name) == MetadataPartitionState::Updated);
-
-    std::string target_type;
-    DmTargetSnapshot::Status status;
-    if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
-        return UpdateState::MergeFailed;
-    }
-    if (target_type != "snapshot-merge") {
-        // We can get here if we failed to rewrite the target type in
-        // InitiateMerge(). If we failed to create the target in first-stage
-        // init, boot would not succeed.
-        LOG(ERROR) << "Snapshot " << name << " has incorrect target type: " << target_type;
-        return UpdateState::MergeFailed;
-    }
-
-    // These two values are equal when merging is complete.
-    if (status.sectors_allocated != status.metadata_sectors) {
-        if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
-            LOG(ERROR) << "Snapshot " << name << " is merging after being marked merge-complete.";
-            return UpdateState::MergeFailed;
-        }
-        return UpdateState::Merging;
-    }
-
-    // Merging is done. First, update the status file to indicate the merge
-    // is complete. We do this before calling OnSnapshotMergeComplete, even
-    // though this means the write is potentially wasted work (since in the
-    // ideal case we'll immediately delete the file).
-    //
-    // This makes it simpler to reason about the next reboot: no matter what
-    // part of cleanup failed, first-stage init won't try to create another
-    // snapshot device for this partition.
-    snapshot_status.set_state(SnapshotState::MERGE_COMPLETED);
-    if (!WriteSnapshotStatus(lock, snapshot_status)) {
-        return UpdateState::MergeFailed;
-    }
-    if (!OnSnapshotMergeComplete(lock, name, snapshot_status)) {
-        return UpdateState::MergeNeedsReboot;
-    }
-    return UpdateState::MergeCompleted;
-}
-
-std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
-    return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
-}
-
-void SnapshotManager::RemoveSnapshotBootIndicator() {
-    // It's okay if this fails - first-stage init performs a deeper check after
-    // reading the indicator file, so it's not a problem if it still exists
-    // after the update completes.
-    auto boot_file = GetSnapshotBootIndicatorPath();
-    if (unlink(boot_file.c_str()) == -1 && errno != ENOENT) {
-        PLOG(ERROR) << "unlink " << boot_file;
-    }
-}
-
-void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
-    RemoveAllUpdateState(lock);
-}
-
-void SnapshotManager::AcknowledgeMergeFailure() {
-    // Log first, so worst case, we always have a record of why the calls below
-    // were being made.
-    LOG(ERROR) << "Merge could not be completed and will be marked as failed.";
-
-    auto lock = LockExclusive();
-    if (!lock) return;
-
-    // Since we released the lock in between WaitForMerge and here, it's
-    // possible (1) the merge successfully completed or (2) was already
-    // marked as a failure. So make sure to check the state again, and
-    // only mark as a failure if appropriate.
-    UpdateState state = ReadUpdateState(lock.get());
-    if (state != UpdateState::Merging && state != UpdateState::MergeNeedsReboot) {
-        return;
-    }
-
-    WriteUpdateState(lock.get(), UpdateState::MergeFailed);
-}
-
-bool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
-                                              const SnapshotStatus& status) {
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    if (IsSnapshotDevice(dm_name)) {
-        // We are extra-cautious here, to avoid deleting the wrong table.
-        std::string target_type;
-        DmTargetSnapshot::Status dm_status;
-        if (!QuerySnapshotStatus(dm_name, &target_type, &dm_status)) {
-            return false;
-        }
-        if (target_type != "snapshot-merge") {
-            LOG(ERROR) << "Unexpected target type " << target_type
-                       << " for snapshot device: " << dm_name;
-            return false;
-        }
-        if (dm_status.sectors_allocated != dm_status.metadata_sectors) {
-            LOG(ERROR) << "Merge is unexpectedly incomplete for device " << dm_name;
-            return false;
-        }
-        if (!CollapseSnapshotDevice(name, status)) {
-            LOG(ERROR) << "Unable to collapse snapshot: " << name;
-            return false;
-        }
-        // Note that collapsing is implicitly an Unmap, so we don't need to
-        // unmap the snapshot.
-    }
-
-    if (!DeleteSnapshot(lock, name)) {
-        LOG(ERROR) << "Could not delete snapshot: " << name;
-        return false;
-    }
-    return true;
-}
-
-bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
-                                             const SnapshotStatus& status) {
-    auto& dm = DeviceMapper::Instance();
-    auto dm_name = GetSnapshotDeviceName(name, status);
-
-    // Verify we have a snapshot-merge device.
-    DeviceMapper::TargetInfo target;
-    if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
-        return false;
-    }
-    if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
-        // This should be impossible, it was checked earlier.
-        LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
-        return false;
-    }
-
-    std::string base_device, cow_device;
-    if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {
-        LOG(ERROR) << "Could not parse snapshot device " << dm_name
-                   << " parameters: " << target.data;
-        return false;
-    }
-
-    uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
-    if (snapshot_sectors * kSectorSize != status.snapshot_size()) {
-        LOG(ERROR) << "Snapshot " << name
-                   << " size is not sector aligned: " << status.snapshot_size();
-        return false;
-    }
-
-    if (dm_name != name) {
-        // We've derived the base device, but we actually need to replace the
-        // table of the outermost device. Do a quick verification that this
-        // device looks like we expect it to.
-        std::vector<DeviceMapper::TargetInfo> outer_table;
-        if (!dm.GetTableInfo(name, &outer_table)) {
-            LOG(ERROR) << "Could not validate outer snapshot table: " << name;
-            return false;
-        }
-        if (outer_table.size() != 2) {
-            LOG(ERROR) << "Expected 2 dm-linear targets for table " << name
-                       << ", got: " << outer_table.size();
-            return false;
-        }
-        for (const auto& target : outer_table) {
-            auto target_type = DeviceMapper::GetTargetType(target.spec);
-            if (target_type != "linear") {
-                LOG(ERROR) << "Outer snapshot table may only contain linear targets, but " << name
-                           << " has target: " << target_type;
-                return false;
-            }
-        }
-        if (outer_table[0].spec.length != snapshot_sectors) {
-            LOG(ERROR) << "dm-snapshot " << name << " should have " << snapshot_sectors
-                       << " sectors, got: " << outer_table[0].spec.length;
-            return false;
-        }
-        uint64_t expected_device_sectors = status.device_size() / kSectorSize;
-        uint64_t actual_device_sectors = outer_table[0].spec.length + outer_table[1].spec.length;
-        if (expected_device_sectors != actual_device_sectors) {
-            LOG(ERROR) << "Outer device " << name << " should have " << expected_device_sectors
-                       << " sectors, got: " << actual_device_sectors;
-            return false;
-        }
-    }
-
-    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
-    // Create a DmTable that is identical to the base device.
-    CreateLogicalPartitionParams base_device_params{
-            .block_device = device_->GetSuperDevice(slot),
-            .metadata_slot = slot,
-            .partition_name = name,
-            .partition_opener = &device_->GetPartitionOpener(),
-    };
-    DmTable table;
-    if (!CreateDmTable(base_device_params, &table)) {
-        LOG(ERROR) << "Could not create a DmTable for partition: " << name;
-        return false;
-    }
-
-    // Note: we are replacing the *outer* table here, so we do not use dm_name.
-    if (!dm.LoadTableAndActivate(name, table)) {
-        return false;
-    }
-
-    // Attempt to delete the snapshot device if one still exists. Nothing
-    // should be depending on the device, and device-mapper should have
-    // flushed remaining I/O. We could in theory replace with dm-zero (or
-    // re-use the table above), but for now it's better to know why this
-    // would fail.
-    if (dm_name != name && !dm.DeleteDeviceIfExists(dm_name)) {
-        LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
-                   << "reclaimed until after reboot.";
-        return false;
-    }
-
-    // Cleanup the base device as well, since it is no longer used. This does
-    // not block cleanup.
-    auto base_name = GetBaseDeviceName(name);
-    if (!dm.DeleteDeviceIfExists(base_name)) {
-        LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
-    }
-    return true;
-}
-
-bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
-    std::string old_slot;
-    auto boot_file = GetSnapshotBootIndicatorPath();
-    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
-        PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
-        return false;
-    }
-    if (device_->GetSlotSuffix() != old_slot) {
-        // We're booted into the target slot, which means we just rebooted
-        // after applying the update.
-        if (!HandleCancelledUpdateOnNewSlot(lock)) {
-            return false;
-        }
-    }
-
-    // The only way we can get here is if:
-    //  (1) The device rolled back to the previous slot.
-    //  (2) This function was called prematurely before rebooting the device.
-    //  (3) fastboot set_active was used.
-    //  (4) The device updates to the new slot but re-flashed *all* partitions
-    //      in the new slot.
-    //
-    // In any case, delete the snapshots. It may be worth using the boot_control
-    // HAL to differentiate case (2).
-    RemoveAllUpdateState(lock);
-    return true;
-}
-
-std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
-    const auto& opener = device_->GetPartitionOpener();
-    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
-    auto super_device = device_->GetSuperDevice(slot);
-    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
-    if (!metadata) {
-        LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
-        return nullptr;
-    }
-    return metadata;
-}
-
-SnapshotManager::MetadataPartitionState SnapshotManager::GetMetadataPartitionState(
-        const LpMetadata& metadata, const std::string& name) {
-    auto partition = android::fs_mgr::FindPartition(metadata, name);
-    if (!partition) return MetadataPartitionState::None;
-    if (partition->attributes & LP_PARTITION_ATTR_UPDATED) {
-        return MetadataPartitionState::Updated;
-    }
-    return MetadataPartitionState::Flashed;
-}
-
-bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) {
-    std::vector<std::string> snapshots;
-    if (!ListSnapshots(lock, &snapshots)) {
-        LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed "
-                     << "after applying an update. Assuming no snapshots.";
-        // Let HandleCancelledUpdate resets UpdateState.
-        return true;
-    }
-
-    // Attempt to detect re-flashing on each partition.
-    // - If all partitions are re-flashed, we can proceed to cancel the whole update.
-    // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are
-    //   deleted. Caller is responsible for merging the rest of the snapshots.
-    // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots.
-    auto metadata = ReadCurrentMetadata();
-    if (!metadata) return false;
-    bool all_snapshot_cancelled = true;
-    for (const auto& snapshot_name : snapshots) {
-        if (GetMetadataPartitionState(*metadata, snapshot_name) ==
-            MetadataPartitionState::Updated) {
-            LOG(WARNING) << "Cannot cancel update because snapshot" << snapshot_name
-                         << " is in use.";
-            all_snapshot_cancelled = false;
-            continue;
-        }
-        // Delete snapshots for partitions that are re-flashed after the update.
-        LOG(INFO) << "Detected re-flashing of partition " << snapshot_name << ".";
-        if (!DeleteSnapshot(lock, snapshot_name)) {
-            // This is an error, but it is okay to leave the snapshot in the short term.
-            // However, if all_snapshot_cancelled == false after exiting the loop, caller may
-            // initiate merge for this unused snapshot, which is likely to fail.
-            LOG(WARNING) << "Failed to delete snapshot for re-flashed partition " << snapshot_name;
-        }
-    }
-    if (!all_snapshot_cancelled) return false;
-
-    LOG(INFO) << "All partitions are re-flashed after update, removing all update states.";
-    return true;
-}
-
-bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
-    std::vector<std::string> snapshots;
-    if (!ListSnapshots(lock, &snapshots)) {
-        LOG(ERROR) << "Could not list snapshots";
-        return false;
-    }
-
-    bool ok = true;
-    for (const auto& name : snapshots) {
-        ok &= (UnmapPartitionWithSnapshot(lock, name) && DeleteSnapshot(lock, name));
-    }
-    return ok;
-}
-
-UpdateState SnapshotManager::GetUpdateState(double* progress) {
-    // If we've never started an update, the state file won't exist.
-    auto state_file = GetStateFilePath();
-    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
-        return UpdateState::None;
-    }
-
-    auto file = LockShared();
-    if (!file) {
-        return UpdateState::None;
-    }
-
-    auto state = ReadUpdateState(file.get());
-    if (progress) {
-        *progress = 0.0;
-        if (state == UpdateState::Merging) {
-            // :TODO: When merging is implemented, set progress_val.
-        } else if (state == UpdateState::MergeCompleted) {
-            *progress = 100.0;
-        }
-    }
-    return state;
-}
-
-bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots) {
-    CHECK(lock);
-
-    auto dir_path = metadata_dir_ + "/snapshots"s;
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dir_path.c_str()), closedir);
-    if (!dir) {
-        PLOG(ERROR) << "opendir failed: " << dir_path;
-        return false;
-    }
-
-    struct dirent* dp;
-    while ((dp = readdir(dir.get())) != nullptr) {
-        if (dp->d_type != DT_REG) continue;
-        snapshots->emplace_back(dp->d_name);
-    }
-    return true;
-}
-
-bool SnapshotManager::IsSnapshotManagerNeeded() {
-    return access(kBootIndicatorPath, F_OK) == 0;
-}
-
-bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
-    // If we fail to read, we'll wind up using CreateLogicalPartitions, which
-    // will create devices that look like the old slot, except with extra
-    // content at the end of each device. This will confuse dm-verity, and
-    // ultimately we'll fail to boot. Why not make it a fatal error and have
-    // the reason be clearer? Because the indicator file still exists, and
-    // if this was FATAL, reverting to the old slot would be broken.
-    std::string old_slot;
-    auto boot_file = GetSnapshotBootIndicatorPath();
-    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
-        PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
-        return false;
-    }
-    if (device_->GetSlotSuffix() == old_slot) {
-        LOG(INFO) << "Detected slot rollback, will not mount snapshots.";
-        return false;
-    }
-
-    // If we can't read the update state, it's unlikely anything else will
-    // succeed, so this is a fatal error. We'll eventually exhaust boot
-    // attempts and revert to the old slot.
-    auto lock = LockShared();
-    if (!lock) {
-        LOG(FATAL) << "Could not read update state to determine snapshot status";
-        return false;
-    }
-    switch (ReadUpdateState(lock.get())) {
-        case UpdateState::Unverified:
-        case UpdateState::Merging:
-        case UpdateState::MergeFailed:
-            return true;
-        default:
-            return false;
-    }
-}
-
-bool SnapshotManager::CreateLogicalAndSnapshotPartitions(const std::string& super_device) {
-    LOG(INFO) << "Creating logical partitions with snapshots as needed";
-
-    auto lock = LockExclusive();
-    if (!lock) return false;
-
-    const auto& opener = device_->GetPartitionOpener();
-    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
-    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
-    if (!metadata) {
-        LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
-        return false;
-    }
-
-    for (const auto& partition : metadata->partitions) {
-        if (GetPartitionGroupName(metadata->groups[partition.group_index]) == kCowGroupName) {
-            LOG(INFO) << "Skip mapping partition " << GetPartitionName(partition) << " in group "
-                      << kCowGroupName;
-            continue;
-        }
-
-        CreateLogicalPartitionParams params = {
-                .block_device = super_device,
-                .metadata = metadata.get(),
-                .partition = &partition,
-                .partition_opener = &opener,
-        };
-        std::string ignore_path;
-        if (!MapPartitionWithSnapshot(lock.get(), std::move(params), &ignore_path)) {
-            return false;
-        }
-    }
-
-    LOG(INFO) << "Created logical partitions with snapshot.";
-    return true;
-}
-
-static std::chrono::milliseconds GetRemainingTime(
-        const std::chrono::milliseconds& timeout,
-        const std::chrono::time_point<std::chrono::steady_clock>& begin) {
-    // If no timeout is specified, execute all commands without specifying any timeout.
-    if (timeout.count() == 0) return std::chrono::milliseconds(0);
-    auto passed_time = std::chrono::steady_clock::now() - begin;
-    auto remaining_time = timeout - duration_cast<std::chrono::milliseconds>(passed_time);
-    if (remaining_time.count() <= 0) {
-        LOG(ERROR) << "MapPartitionWithSnapshot has reached timeout " << timeout.count() << "ms ("
-                   << remaining_time.count() << "ms remaining)";
-        // Return min() instead of remaining_time here because 0 is treated as a special value for
-        // no timeout, where the rest of the commands will still be executed.
-        return std::chrono::milliseconds::min();
-    }
-    return remaining_time;
-}
-
-bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
-                                               CreateLogicalPartitionParams params,
-                                               std::string* path) {
-    auto begin = std::chrono::steady_clock::now();
-
-    CHECK(lock);
-    path->clear();
-
-    if (params.GetPartitionName() != params.GetDeviceName()) {
-        LOG(ERROR) << "Mapping snapshot with a different name is unsupported: partition_name = "
-                   << params.GetPartitionName() << ", device_name = " << params.GetDeviceName();
-        return false;
-    }
-
-    // Fill out fields in CreateLogicalPartitionParams so that we have more information (e.g. by
-    // reading super partition metadata).
-    CreateLogicalPartitionParams::OwnedData params_owned_data;
-    if (!params.InitDefaults(&params_owned_data)) {
-        return false;
-    }
-
-    if (!params.partition->num_extents) {
-        LOG(INFO) << "Skipping zero-length logical partition: " << params.GetPartitionName();
-        return true;  // leave path empty to indicate that nothing is mapped.
-    }
-
-    // Determine if there is a live snapshot for the SnapshotStatus of the partition; i.e. if the
-    // partition still has a snapshot that needs to be mapped.  If no live snapshot or merge
-    // completed, live_snapshot_status is set to nullopt.
-    std::optional<SnapshotStatus> live_snapshot_status;
-    do {
-        if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
-            LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
-                      << params.GetPartitionName();
-            break;
-        }
-        auto file_path = GetSnapshotStatusFilePath(params.GetPartitionName());
-        if (access(file_path.c_str(), F_OK) != 0) {
-            if (errno != ENOENT) {
-                PLOG(INFO) << "Can't map snapshot for " << params.GetPartitionName()
-                           << ": Can't access " << file_path;
-                return false;
-            }
-            break;
-        }
-        live_snapshot_status = std::make_optional<SnapshotStatus>();
-        if (!ReadSnapshotStatus(lock, params.GetPartitionName(), &*live_snapshot_status)) {
-            return false;
-        }
-        // No live snapshot if merge is completed.
-        if (live_snapshot_status->state() == SnapshotState::MERGE_COMPLETED) {
-            live_snapshot_status.reset();
-        }
-    } while (0);
-
-    if (live_snapshot_status.has_value()) {
-        // dm-snapshot requires the base device to be writable.
-        params.force_writable = true;
-        // Map the base device with a different name to avoid collision.
-        params.device_name = GetBaseDeviceName(params.GetPartitionName());
-    }
-
-    AutoDeviceList created_devices;
-
-    // Create the base device for the snapshot, or if there is no snapshot, the
-    // device itself. This device consists of the real blocks in the super
-    // partition that this logical partition occupies.
-    auto& dm = DeviceMapper::Instance();
-    std::string base_path;
-    if (!CreateLogicalPartition(params, &base_path)) {
-        LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
-                   << " as device " << params.GetDeviceName();
-        return false;
-    }
-    created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
-
-    if (!live_snapshot_status.has_value()) {
-        *path = base_path;
-        created_devices.Release();
-        return true;
-    }
-
-    // We don't have ueventd in first-stage init, so use device major:minor
-    // strings instead.
-    std::string base_device;
-    if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
-        LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
-        return false;
-    }
-
-    auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
-    if (remaining_time.count() < 0) return false;
-
-    std::string cow_name;
-    CreateLogicalPartitionParams cow_params = params;
-    cow_params.timeout_ms = remaining_time;
-    if (!MapCowDevices(lock, cow_params, *live_snapshot_status, &created_devices, &cow_name)) {
-        return false;
-    }
-    std::string cow_device;
-    if (!dm.GetDeviceString(cow_name, &cow_device)) {
-        LOG(ERROR) << "Could not determine major/minor for: " << cow_name;
-        return false;
-    }
-
-    remaining_time = GetRemainingTime(params.timeout_ms, begin);
-    if (remaining_time.count() < 0) return false;
-
-    if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
-                     path)) {
-        LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
-        return false;
-    }
-    // No need to add params.GetPartitionName() to created_devices since it is immediately released.
-
-    created_devices.Release();
-
-    LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << *path;
-
-    return true;
-}
-
-bool SnapshotManager::UnmapPartitionWithSnapshot(LockedFile* lock,
-                                                 const std::string& target_partition_name) {
-    CHECK(lock);
-
-    if (!UnmapSnapshot(lock, target_partition_name)) {
-        return false;
-    }
-
-    if (!UnmapCowDevices(lock, target_partition_name)) {
-        return false;
-    }
-
-    auto& dm = DeviceMapper::Instance();
-    std::string base_name = GetBaseDeviceName(target_partition_name);
-    if (!dm.DeleteDeviceIfExists(base_name)) {
-        LOG(ERROR) << "Cannot delete base device: " << base_name;
-        return false;
-    }
-
-    LOG(INFO) << "Successfully unmapped snapshot " << target_partition_name;
-
-    return true;
-}
-
-bool SnapshotManager::MapCowDevices(LockedFile* lock, const CreateLogicalPartitionParams& params,
-                                    const SnapshotStatus& snapshot_status,
-                                    AutoDeviceList* created_devices, std::string* cow_name) {
-    CHECK(lock);
-    CHECK(snapshot_status.cow_partition_size() + snapshot_status.cow_file_size() > 0);
-    auto begin = std::chrono::steady_clock::now();
-
-    std::string partition_name = params.GetPartitionName();
-    std::string cow_image_name = GetCowImageDeviceName(partition_name);
-    *cow_name = GetCowName(partition_name);
-
-    auto& dm = DeviceMapper::Instance();
-
-    // Map COW image if necessary.
-    if (snapshot_status.cow_file_size() > 0) {
-        if (!EnsureImageManager()) return false;
-        auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
-        if (remaining_time.count() < 0) return false;
-
-        if (!MapCowImage(partition_name, remaining_time).has_value()) {
-            LOG(ERROR) << "Could not map cow image for partition: " << partition_name;
-            return false;
-        }
-        created_devices->EmplaceBack<AutoUnmapImage>(images_.get(), cow_image_name);
-
-        // If no COW partition exists, just return the image alone.
-        if (snapshot_status.cow_partition_size() == 0) {
-            *cow_name = std::move(cow_image_name);
-            LOG(INFO) << "Mapped COW image for " << partition_name << " at " << *cow_name;
-            return true;
-        }
-    }
-
-    auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
-    if (remaining_time.count() < 0) return false;
-
-    CHECK(snapshot_status.cow_partition_size() > 0);
-
-    // Create the DmTable for the COW device. It is the DmTable of the COW partition plus
-    // COW image device as the last extent.
-    CreateLogicalPartitionParams cow_partition_params = params;
-    cow_partition_params.partition = nullptr;
-    cow_partition_params.partition_name = *cow_name;
-    cow_partition_params.device_name.clear();
-    DmTable table;
-    if (!CreateDmTable(cow_partition_params, &table)) {
-        return false;
-    }
-    // If the COW image exists, append it as the last extent.
-    if (snapshot_status.cow_file_size() > 0) {
-        std::string cow_image_device;
-        if (!dm.GetDeviceString(cow_image_name, &cow_image_device)) {
-            LOG(ERROR) << "Cannot determine major/minor for: " << cow_image_name;
-            return false;
-        }
-        auto cow_partition_sectors = snapshot_status.cow_partition_size() / kSectorSize;
-        auto cow_image_sectors = snapshot_status.cow_file_size() / kSectorSize;
-        table.Emplace<DmTargetLinear>(cow_partition_sectors, cow_image_sectors, cow_image_device,
-                                      0);
-    }
-
-    // We have created the DmTable now. Map it.
-    std::string cow_path;
-    if (!dm.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {
-        LOG(ERROR) << "Could not create COW device: " << *cow_name;
-        return false;
-    }
-    created_devices->EmplaceBack<AutoUnmapDevice>(&dm, *cow_name);
-    LOG(INFO) << "Mapped COW device for " << params.GetPartitionName() << " at " << cow_path;
-    return true;
-}
-
-bool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name) {
-    CHECK(lock);
-    if (!EnsureImageManager()) return false;
-
-    auto& dm = DeviceMapper::Instance();
-    auto cow_name = GetCowName(name);
-    if (!dm.DeleteDeviceIfExists(cow_name)) {
-        LOG(ERROR) << "Cannot unmap " << cow_name;
-        return false;
-    }
-
-    std::string cow_image_name = GetCowImageDeviceName(name);
-    if (!images_->UnmapImageIfExists(cow_image_name)) {
-        LOG(ERROR) << "Cannot unmap image " << cow_image_name;
-        return false;
-    }
-    return true;
-}
-
-auto SnapshotManager::OpenFile(const std::string& file, int open_flags, int lock_flags)
-        -> std::unique_ptr<LockedFile> {
-    unique_fd fd(open(file.c_str(), open_flags | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0660));
-    if (fd < 0) {
-        PLOG(ERROR) << "Open failed: " << file;
-        return nullptr;
-    }
-    if (lock_flags != 0 && flock(fd, lock_flags) < 0) {
-        PLOG(ERROR) << "Acquire flock failed: " << file;
-        return nullptr;
-    }
-    // For simplicity, we want to CHECK that lock_mode == LOCK_EX, in some
-    // calls, so strip extra flags.
-    int lock_mode = lock_flags & (LOCK_EX | LOCK_SH);
-    return std::make_unique<LockedFile>(file, std::move(fd), lock_mode);
-}
-
-SnapshotManager::LockedFile::~LockedFile() {
-    if (flock(fd_, LOCK_UN) < 0) {
-        PLOG(ERROR) << "Failed to unlock file: " << path_;
-    }
-}
-
-std::string SnapshotManager::GetStateFilePath() const {
-    return metadata_dir_ + "/state"s;
-}
-
-std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::OpenStateFile(int open_flags,
-                                                                            int lock_flags) {
-    auto state_file = GetStateFilePath();
-    return OpenFile(state_file, open_flags, lock_flags);
-}
-
-std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::LockShared() {
-    return OpenStateFile(O_RDONLY, LOCK_SH);
-}
-
-std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::LockExclusive() {
-    return OpenStateFile(O_RDWR | O_CREAT, LOCK_EX);
-}
-
-UpdateState SnapshotManager::ReadUpdateState(LockedFile* file) {
-    // Reset position since some calls read+write.
-    if (lseek(file->fd(), 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek state file failed";
-        return UpdateState::None;
-    }
-
-    std::string contents;
-    if (!android::base::ReadFdToString(file->fd(), &contents)) {
-        PLOG(ERROR) << "Read state file failed";
-        return UpdateState::None;
-    }
-
-    if (contents.empty() || contents == "none") {
-        return UpdateState::None;
-    } else if (contents == "initiated") {
-        return UpdateState::Initiated;
-    } else if (contents == "unverified") {
-        return UpdateState::Unverified;
-    } else if (contents == "merging") {
-        return UpdateState::Merging;
-    } else if (contents == "merge-completed") {
-        return UpdateState::MergeCompleted;
-    } else if (contents == "merge-needs-reboot") {
-        return UpdateState::MergeNeedsReboot;
-    } else if (contents == "merge-failed") {
-        return UpdateState::MergeFailed;
-    } else {
-        LOG(ERROR) << "Unknown merge state in update state file";
-        return UpdateState::None;
-    }
-}
-
-std::ostream& operator<<(std::ostream& os, UpdateState state) {
-    switch (state) {
-        case UpdateState::None:
-            return os << "none";
-        case UpdateState::Initiated:
-            return os << "initiated";
-        case UpdateState::Unverified:
-            return os << "unverified";
-        case UpdateState::Merging:
-            return os << "merging";
-        case UpdateState::MergeCompleted:
-            return os << "merge-completed";
-        case UpdateState::MergeNeedsReboot:
-            return os << "merge-needs-reboot";
-        case UpdateState::MergeFailed:
-            return os << "merge-failed";
-        default:
-            LOG(ERROR) << "Unknown update state";
-            return os;
-    }
-}
-
-bool SnapshotManager::WriteUpdateState(LockedFile* file, UpdateState state) {
-    std::stringstream ss;
-    ss << state;
-    std::string contents = ss.str();
-    if (contents.empty()) return false;
-
-    if (!Truncate(file)) return false;
-    if (!android::base::WriteStringToFd(contents, file->fd())) {
-        PLOG(ERROR) << "Could not write to state file";
-        return false;
-    }
-
-#ifdef LIBSNAPSHOT_USE_HAL
-    auto merge_status = MergeStatus::UNKNOWN;
-    switch (state) {
-        // The needs-reboot and completed cases imply that /data and /metadata
-        // can be safely wiped, so we don't report a merge status.
-        case UpdateState::None:
-        case UpdateState::MergeNeedsReboot:
-        case UpdateState::MergeCompleted:
-            merge_status = MergeStatus::NONE;
-            break;
-        case UpdateState::Initiated:
-        case UpdateState::Unverified:
-            merge_status = MergeStatus::SNAPSHOTTED;
-            break;
-        case UpdateState::Merging:
-        case UpdateState::MergeFailed:
-            merge_status = MergeStatus::MERGING;
-            break;
-        default:
-            // Note that Cancelled flows to here - it is never written, since
-            // it only communicates a transient state to the caller.
-            LOG(ERROR) << "Unexpected update status: " << state;
-            break;
-    }
-    if (!device_->SetBootControlMergeStatus(merge_status)) {
-        return false;
-    }
-#endif
-    return true;
-}
-
-std::string SnapshotManager::GetSnapshotStatusFilePath(const std::string& name) {
-    auto file = metadata_dir_ + "/snapshots/"s + name;
-    return file;
-}
-
-bool SnapshotManager::ReadSnapshotStatus(LockedFile* lock, const std::string& name,
-                                         SnapshotStatus* status) {
-    CHECK(lock);
-    auto path = GetSnapshotStatusFilePath(name);
-
-    unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
-    if (fd < 0) {
-        PLOG(ERROR) << "Open failed: " << path;
-        return false;
-    }
-
-    if (!status->ParseFromFileDescriptor(fd.get())) {
-        PLOG(ERROR) << "Unable to parse " << path << " as SnapshotStatus";
-        return false;
-    }
-
-    if (status->name() != name) {
-        LOG(WARNING) << "Found snapshot status named " << status->name() << " in " << path;
-        status->set_name(name);
-    }
-
-    return true;
-}
-
-bool SnapshotManager::WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status) {
-    // The caller must take an exclusive lock to modify snapshots.
-    CHECK(lock);
-    CHECK(lock->lock_mode() == LOCK_EX);
-    CHECK(!status.name().empty());
-
-    auto path = GetSnapshotStatusFilePath(status.name());
-    unique_fd fd(
-            open(path.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW | O_CREAT | O_SYNC | O_TRUNC, 0660));
-    if (fd < 0) {
-        PLOG(ERROR) << "Open failed: " << path;
-        return false;
-    }
-
-    if (!status.SerializeToFileDescriptor(fd.get())) {
-        PLOG(ERROR) << "Unable to write SnapshotStatus to " << path;
-        return false;
-    }
-
-    return true;
-}
-
-bool SnapshotManager::Truncate(LockedFile* file) {
-    if (lseek(file->fd(), 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek file failed: " << file->path();
-        return false;
-    }
-    if (ftruncate(file->fd(), 0) < 0) {
-        PLOG(ERROR) << "truncate failed: " << file->path();
-        return false;
-    }
-    return true;
-}
-
-std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
-                                                   const SnapshotStatus& status) {
-    if (status.device_size() != status.snapshot_size()) {
-        return GetSnapshotExtraDeviceName(snapshot_name);
-    }
-    return snapshot_name;
-}
-
-bool SnapshotManager::EnsureImageManager() {
-    if (images_) return true;
-
-    // For now, use a preset timeout.
-    images_ = android::fiemap::IImageManager::Open(gsid_dir_, 15000ms);
-    if (!images_) {
-        LOG(ERROR) << "Could not open ImageManager";
-        return false;
-    }
-    return true;
-}
-
-bool SnapshotManager::ForceLocalImageManager() {
-    images_ = android::fiemap::ImageManager::Open(gsid_dir_);
-    if (!images_) {
-        LOG(ERROR) << "Could not open ImageManager";
-        return false;
-    }
-    has_local_image_manager_ = true;
-    return true;
-}
-
-static void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {
-    auto& dm = DeviceMapper::Instance();
-    std::vector<std::string> to_delete;
-    for (auto* existing_cow_partition : current_metadata->ListPartitionsInGroup(kCowGroupName)) {
-        if (!dm.DeleteDeviceIfExists(existing_cow_partition->name())) {
-            LOG(WARNING) << existing_cow_partition->name()
-                         << " cannot be unmapped and its space cannot be reclaimed";
-            continue;
-        }
-        to_delete.push_back(existing_cow_partition->name());
-    }
-    for (const auto& name : to_delete) {
-        current_metadata->RemovePartition(name);
-    }
-}
-
-bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
-    auto lock = LockExclusive();
-    if (!lock) return false;
-
-    // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
-    // partition takes up a big chunk of space in super, causing COW images to be created on
-    // retrofit Virtual A/B devices.
-    if (device_->IsOverlayfsSetup()) {
-        LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
-                   << ", reboot, then try again.";
-        return false;
-    }
-
-    const auto& opener = device_->GetPartitionOpener();
-    auto current_suffix = device_->GetSlotSuffix();
-    uint32_t current_slot = SlotNumberForSlotSuffix(current_suffix);
-    auto target_suffix = device_->GetOtherSlotSuffix();
-    uint32_t target_slot = SlotNumberForSlotSuffix(target_suffix);
-    auto current_super = device_->GetSuperDevice(current_slot);
-
-    auto current_metadata = MetadataBuilder::New(opener, current_super, current_slot);
-    auto target_metadata =
-            MetadataBuilder::NewForUpdate(opener, current_super, current_slot, target_slot);
-
-    // Delete partitions with target suffix in |current_metadata|. Otherwise,
-    // partition_cow_creator recognizes these left-over partitions as used space.
-    for (const auto& group_name : current_metadata->ListGroups()) {
-        if (android::base::EndsWith(group_name, target_suffix)) {
-            current_metadata->RemoveGroupAndPartitions(group_name);
-        }
-    }
-
-    SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
-    if (!metadata_updater.Update()) {
-        LOG(ERROR) << "Cannot calculate new metadata.";
-        return false;
-    }
-
-    // Delete previous COW partitions in current_metadata so that PartitionCowCreator marks those as
-    // free regions.
-    UnmapAndDeleteCowPartition(current_metadata.get());
-
-    // Check that all these metadata is not retrofit dynamic partitions. Snapshots on
-    // devices with retrofit dynamic partitions does not make sense.
-    // This ensures that current_metadata->GetFreeRegions() uses the same device
-    // indices as target_metadata (i.e. 0 -> "super").
-    // This is also assumed in MapCowDevices() call below.
-    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
-          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
-
-    std::map<std::string, SnapshotStatus> all_snapshot_status;
-
-    // In case of error, automatically delete devices that are created along the way.
-    // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
-    // these devices.
-    AutoDeviceList created_devices;
-
-    PartitionCowCreator cow_creator{.target_metadata = target_metadata.get(),
-                                    .target_suffix = target_suffix,
-                                    .target_partition = nullptr,
-                                    .current_metadata = current_metadata.get(),
-                                    .current_suffix = current_suffix,
-                                    .operations = nullptr};
-
-    if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
-                                       &all_snapshot_status)) {
-        return false;
-    }
-
-    auto exported_target_metadata = target_metadata->Export();
-    if (exported_target_metadata == nullptr) {
-        LOG(ERROR) << "Cannot export target metadata";
-        return false;
-    }
-
-    if (!InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
-                                   exported_target_metadata.get(), target_suffix,
-                                   all_snapshot_status)) {
-        return false;
-    }
-
-    if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
-                              *exported_target_metadata, target_slot)) {
-        LOG(ERROR) << "Cannot write target metadata";
-        return false;
-    }
-
-    created_devices.Release();
-    LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
-
-    return true;
-}
-
-bool SnapshotManager::CreateUpdateSnapshotsInternal(
-        LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,
-        AutoDeviceList* created_devices,
-        std::map<std::string, SnapshotStatus>* all_snapshot_status) {
-    CHECK(lock);
-
-    auto* target_metadata = cow_creator->target_metadata;
-    const auto& target_suffix = cow_creator->target_suffix;
-
-    if (!target_metadata->AddGroup(kCowGroupName, 0)) {
-        LOG(ERROR) << "Cannot add group " << kCowGroupName;
-        return false;
-    }
-
-    std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
-    for (const auto& partition_update : manifest.partitions()) {
-        auto suffixed_name = partition_update.partition_name() + target_suffix;
-        auto&& [it, inserted] = install_operation_map.emplace(std::move(suffixed_name),
-                                                              &partition_update.operations());
-        if (!inserted) {
-            LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
-                       << " in update manifest.";
-            return false;
-        }
-    }
-
-    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
-        cow_creator->target_partition = target_partition;
-        cow_creator->operations = nullptr;
-        auto operations_it = install_operation_map.find(target_partition->name());
-        if (operations_it != install_operation_map.end()) {
-            cow_creator->operations = operations_it->second;
-        }
-
-        // Compute the device sizes for the partition.
-        auto cow_creator_ret = cow_creator->Run();
-        if (!cow_creator_ret.has_value()) {
-            return false;
-        }
-
-        LOG(INFO) << "For partition " << target_partition->name()
-                  << ", device size = " << cow_creator_ret->snapshot_status.device_size()
-                  << ", snapshot size = " << cow_creator_ret->snapshot_status.snapshot_size()
-                  << ", cow partition size = "
-                  << cow_creator_ret->snapshot_status.cow_partition_size()
-                  << ", cow file size = " << cow_creator_ret->snapshot_status.cow_file_size();
-
-        // Delete any existing snapshot before re-creating one.
-        if (!DeleteSnapshot(lock, target_partition->name())) {
-            LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
-                       << target_partition->name();
-            return false;
-        }
-
-        // It is possible that the whole partition uses free space in super, and snapshot / COW
-        // would not be needed. In this case, skip the partition.
-        bool needs_snapshot = cow_creator_ret->snapshot_status.snapshot_size() > 0;
-        bool needs_cow = (cow_creator_ret->snapshot_status.cow_partition_size() +
-                          cow_creator_ret->snapshot_status.cow_file_size()) > 0;
-        CHECK(needs_snapshot == needs_cow);
-
-        if (!needs_snapshot) {
-            LOG(INFO) << "Skip creating snapshot for partition " << target_partition->name()
-                      << "because nothing needs to be snapshotted.";
-            continue;
-        }
-
-        // Store these device sizes to snapshot status file.
-        if (!CreateSnapshot(lock, &cow_creator_ret->snapshot_status)) {
-            return false;
-        }
-        created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());
-
-        // Create the COW partition. That is, use any remaining free space in super partition before
-        // creating the COW images.
-        if (cow_creator_ret->snapshot_status.cow_partition_size() > 0) {
-            CHECK(cow_creator_ret->snapshot_status.cow_partition_size() % kSectorSize == 0)
-                    << "cow_partition_size == "
-                    << cow_creator_ret->snapshot_status.cow_partition_size()
-                    << " is not a multiple of sector size " << kSectorSize;
-            auto cow_partition = target_metadata->AddPartition(GetCowName(target_partition->name()),
-                                                               kCowGroupName, 0 /* flags */);
-            if (cow_partition == nullptr) {
-                return false;
-            }
-
-            if (!target_metadata->ResizePartition(
-                        cow_partition, cow_creator_ret->snapshot_status.cow_partition_size(),
-                        cow_creator_ret->cow_partition_usable_regions)) {
-                LOG(ERROR) << "Cannot create COW partition on metadata with size "
-                           << cow_creator_ret->snapshot_status.cow_partition_size();
-                return false;
-            }
-            // Only the in-memory target_metadata is modified; nothing to clean up if there is an
-            // error in the future.
-        }
-
-        // Create the backing COW image if necessary.
-        if (cow_creator_ret->snapshot_status.cow_file_size() > 0) {
-            if (!CreateCowImage(lock, target_partition->name())) {
-                return false;
-            }
-        }
-
-        all_snapshot_status->emplace(target_partition->name(),
-                                     std::move(cow_creator_ret->snapshot_status));
-
-        LOG(INFO) << "Successfully created snapshot for " << target_partition->name();
-    }
-    return true;
-}
-
-bool SnapshotManager::InitializeUpdateSnapshots(
-        LockedFile* lock, MetadataBuilder* target_metadata,
-        const LpMetadata* exported_target_metadata, const std::string& target_suffix,
-        const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
-    CHECK(lock);
-
-    auto& dm = DeviceMapper::Instance();
-    CreateLogicalPartitionParams cow_params{
-            .block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
-            .metadata = exported_target_metadata,
-            .timeout_ms = std::chrono::milliseconds::max(),
-            .partition_opener = &device_->GetPartitionOpener(),
-    };
-    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
-        AutoDeviceList created_devices_for_cow;
-
-        if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {
-            LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
-                       << target_partition->name();
-            return false;
-        }
-
-        auto it = all_snapshot_status.find(target_partition->name());
-        if (it == all_snapshot_status.end()) continue;
-        cow_params.partition_name = target_partition->name();
-        std::string cow_name;
-        if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {
-            return false;
-        }
-
-        std::string cow_path;
-        if (!dm.GetDmDevicePathByName(cow_name, &cow_path)) {
-            LOG(ERROR) << "Cannot determine path for " << cow_name;
-            return false;
-        }
-
-        if (!InitializeCow(cow_path)) {
-            LOG(ERROR) << "Can't zero-fill COW device for " << target_partition->name() << ": "
-                       << cow_path;
-            return false;
-        }
-        // Let destructor of created_devices_for_cow to unmap the COW devices.
-    };
-    return true;
-}
-
-bool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
-                                        std::string* snapshot_path) {
-    auto lock = LockShared();
-    if (!lock) return false;
-    if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {
-        LOG(ERROR) << "Cannot unmap existing snapshot before re-mapping it: "
-                   << params.GetPartitionName();
-        return false;
-    }
-    return MapPartitionWithSnapshot(lock.get(), params, snapshot_path);
-}
-
-bool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {
-    auto lock = LockShared();
-    if (!lock) return false;
-    return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
-}
-
-bool SnapshotManager::Dump(std::ostream& os) {
-    // Don't actually lock. Dump() is for debugging purposes only, so it is okay
-    // if it is racy.
-    auto file = OpenStateFile(O_RDONLY, 0);
-    if (!file) return false;
-
-    std::stringstream ss;
-
-    ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
-
-    auto boot_file = GetSnapshotBootIndicatorPath();
-    std::string boot_indicator;
-    if (android::base::ReadFileToString(boot_file, &boot_indicator)) {
-        ss << "Boot indicator: old slot = " << boot_indicator << std::endl;
-    }
-
-    bool ok = true;
-    std::vector<std::string> snapshots;
-    if (!ListSnapshots(file.get(), &snapshots)) {
-        LOG(ERROR) << "Could not list snapshots";
-        snapshots.clear();
-        ok = false;
-    }
-    for (const auto& name : snapshots) {
-        ss << "Snapshot: " << name << std::endl;
-        SnapshotStatus status;
-        if (!ReadSnapshotStatus(file.get(), name, &status)) {
-            ok = false;
-            continue;
-        }
-        ss << "    state: " << SnapshotState_Name(status.state()) << std::endl;
-        ss << "    device size (bytes): " << status.device_size() << std::endl;
-        ss << "    snapshot size (bytes): " << status.snapshot_size() << std::endl;
-        ss << "    cow partition size (bytes): " << status.cow_partition_size() << std::endl;
-        ss << "    cow file size (bytes): " << status.cow_file_size() << std::endl;
-        ss << "    allocated sectors: " << status.sectors_allocated() << std::endl;
-        ss << "    metadata sectors: " << status.metadata_sectors() << std::endl;
-    }
-    os << ss.rdbuf();
-    return ok;
-}
-
-std::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() {
-    if (!device_->IsRecovery()) {
-        // No need to mount anything in recovery.
-        LOG(INFO) << "EnsureMetadataMounted does nothing in Android mode.";
-        return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());
-    }
-    return AutoUnmountDevice::New(device_->GetMetadataDir());
-}
-
-UpdateState SnapshotManager::InitiateMergeAndWait() {
-    LOG(INFO) << "Waiting for any previous merge request to complete. "
-              << "This can take up to several minutes.";
-    auto state = ProcessUpdateState();
-    if (state == UpdateState::None) {
-        LOG(INFO) << "Can't find any snapshot to merge.";
-        return state;
-    }
-    if (state == UpdateState::Unverified) {
-        if (!InitiateMerge()) {
-            LOG(ERROR) << "Failed to initiate merge.";
-            return state;
-        }
-        // All other states can be handled by ProcessUpdateState.
-        LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
-        state = ProcessUpdateState();
-    }
-
-    LOG(INFO) << "Merge finished with state \"" << state << "\".";
-    return state;
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
deleted file mode 100644
index 60bf796..0000000
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//
-// 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 "snapshot_metadata_updater.h"
-
-#include <algorithm>
-#include <map>
-#include <optional>
-#include <set>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <fs_mgr.h>
-#include <libsnapshot/snapshot.h>
-
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::Partition;
-using android::fs_mgr::SlotSuffixForSlotNumber;
-using chromeos_update_engine::DeltaArchiveManifest;
-
-namespace android {
-namespace snapshot {
-SnapshotMetadataUpdater::SnapshotMetadataUpdater(MetadataBuilder* builder, uint32_t target_slot,
-                                                 const DeltaArchiveManifest& manifest)
-    : builder_(builder), target_suffix_(SlotSuffixForSlotNumber(target_slot)) {
-    if (!manifest.has_dynamic_partition_metadata()) {
-        return;
-    }
-
-    // Key: partition name ("system"). Value: group name ("group").
-    // No suffix.
-    std::map<std::string_view, std::string_view> partition_group_map;
-    const auto& metadata_groups = manifest.dynamic_partition_metadata().groups();
-    groups_.reserve(metadata_groups.size());
-    for (const auto& group : metadata_groups) {
-        groups_.emplace_back(Group{group.name() + target_suffix_, &group});
-        for (const auto& partition_name : group.partition_names()) {
-            partition_group_map[partition_name] = group.name();
-        }
-    }
-
-    for (const auto& p : manifest.partitions()) {
-        auto it = partition_group_map.find(p.partition_name());
-        if (it != partition_group_map.end()) {
-            partitions_.emplace_back(Partition{p.partition_name() + target_suffix_,
-                                               std::string(it->second) + target_suffix_, &p});
-        }
-    }
-}
-
-bool SnapshotMetadataUpdater::ShrinkPartitions() const {
-    for (const auto& partition_update : partitions_) {
-        auto* existing_partition = builder_->FindPartition(partition_update.name);
-        if (existing_partition == nullptr) {
-            continue;
-        }
-        auto new_size = partition_update->new_partition_info().size();
-        if (existing_partition->size() <= new_size) {
-            continue;
-        }
-        if (!builder_->ResizePartition(existing_partition, new_size)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::DeletePartitions() const {
-    std::vector<std::string> partitions_to_delete;
-    // Don't delete partitions in groups where the group name doesn't have target_suffix,
-    // e.g. default.
-    for (auto* existing_partition : ListPartitionsWithSuffix(builder_, target_suffix_)) {
-        auto iter = std::find_if(partitions_.begin(), partitions_.end(),
-                                 [existing_partition](auto&& partition_update) {
-                                     return partition_update.name == existing_partition->name();
-                                 });
-        // Update package metadata doesn't have this partition. Prepare to delete it.
-        // Not deleting from builder_ yet because it may break ListPartitionsWithSuffix if it were
-        // to return an iterable view of builder_.
-        if (iter == partitions_.end()) {
-            partitions_to_delete.push_back(existing_partition->name());
-        }
-    }
-
-    for (const auto& partition_name : partitions_to_delete) {
-        builder_->RemovePartition(partition_name);
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::MovePartitionsToDefault() const {
-    for (const auto& partition_update : partitions_) {
-        auto* existing_partition = builder_->FindPartition(partition_update.name);
-        if (existing_partition == nullptr) {
-            continue;
-        }
-        if (existing_partition->group_name() == partition_update.group_name) {
-            continue;
-        }
-        // Move to "default" group (which doesn't have maximum size constraint)
-        // temporarily.
-        if (!builder_->ChangePartitionGroup(existing_partition, android::fs_mgr::kDefaultGroup)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::ShrinkGroups() const {
-    for (const auto& group_update : groups_) {
-        auto* existing_group = builder_->FindGroup(group_update.name);
-        if (existing_group == nullptr) {
-            continue;
-        }
-        if (existing_group->maximum_size() <= group_update->size()) {
-            continue;
-        }
-        if (!builder_->ChangeGroupSize(existing_group->name(), group_update->size())) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::DeleteGroups() const {
-    std::vector<std::string> existing_groups = builder_->ListGroups();
-    for (const auto& existing_group_name : existing_groups) {
-        // Don't delete groups without target suffix, e.g. default.
-        if (!android::base::EndsWith(existing_group_name, target_suffix_)) {
-            continue;
-        }
-
-        auto iter = std::find_if(groups_.begin(), groups_.end(),
-                                 [&existing_group_name](auto&& group_update) {
-                                     return group_update.name == existing_group_name;
-                                 });
-        // Update package metadata has this group as well, so not deleting it.
-        if (iter != groups_.end()) {
-            continue;
-        }
-        // Update package metadata doesn't have this group. Before deleting it, sanity check that it
-        // doesn't have any partitions left. Update metadata shouldn't assign any partitions to this
-        // group, so all partitions that originally belong to this group should be moved by
-        // MovePartitionsToDefault at this point.
-        auto existing_partitions_in_group = builder_->ListPartitionsInGroup(existing_group_name);
-        if (!existing_partitions_in_group.empty()) {
-            std::vector<std::string> partition_names_in_group;
-            std::transform(existing_partitions_in_group.begin(), existing_partitions_in_group.end(),
-                           std::back_inserter(partition_names_in_group),
-                           [](auto* p) { return p->name(); });
-            LOG(ERROR)
-                    << "Group " << existing_group_name
-                    << " cannot be deleted because the following partitions are left unassigned: ["
-                    << android::base::Join(partition_names_in_group, ",") << "]";
-            return false;
-        }
-        builder_->RemoveGroupAndPartitions(existing_group_name);
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::AddGroups() const {
-    for (const auto& group_update : groups_) {
-        if (builder_->FindGroup(group_update.name) == nullptr) {
-            if (!builder_->AddGroup(group_update.name, group_update->size())) {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::GrowGroups() const {
-    for (const auto& group_update : groups_) {
-        auto* existing_group = builder_->FindGroup(group_update.name);
-        if (existing_group == nullptr) {
-            continue;
-        }
-        if (existing_group->maximum_size() >= group_update->size()) {
-            continue;
-        }
-        if (!builder_->ChangeGroupSize(existing_group->name(), group_update->size())) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::AddPartitions() const {
-    for (const auto& partition_update : partitions_) {
-        if (builder_->FindPartition(partition_update.name) == nullptr) {
-            auto* p =
-                    builder_->AddPartition(partition_update.name, partition_update.group_name,
-                                           LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_UPDATED);
-            if (p == nullptr) {
-                return false;
-            }
-        }
-    }
-    // Will be resized in GrowPartitions.
-    return true;
-}
-
-bool SnapshotMetadataUpdater::GrowPartitions() const {
-    for (const auto& partition_update : partitions_) {
-        auto* existing_partition = builder_->FindPartition(partition_update.name);
-        if (existing_partition == nullptr) {
-            continue;
-        }
-        auto new_size = partition_update->new_partition_info().size();
-        if (existing_partition->size() >= new_size) {
-            continue;
-        }
-        if (!builder_->ResizePartition(existing_partition, new_size)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::MovePartitionsToCorrectGroup() const {
-    for (const auto& partition_update : partitions_) {
-        auto* existing_partition = builder_->FindPartition(partition_update.name);
-        if (existing_partition == nullptr) {
-            continue;
-        }
-        if (existing_partition->group_name() == partition_update.group_name) {
-            continue;
-        }
-        if (!builder_->ChangePartitionGroup(existing_partition, partition_update.group_name)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SnapshotMetadataUpdater::Update() const {
-    // Remove extents used by COW devices by removing the COW group completely.
-    builder_->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);
-
-    // The order of these operations are important so that we
-    // always have enough space to grow or add new partitions / groups.
-    // clang-format off
-    return ShrinkPartitions() &&
-           DeletePartitions() &&
-           MovePartitionsToDefault() &&
-           ShrinkGroups() &&
-           DeleteGroups() &&
-           AddGroups() &&
-           GrowGroups() &&
-           AddPartitions() &&
-           GrowPartitions() &&
-           MovePartitionsToCorrectGroup();
-    // clang-format on
-}
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater.h b/fs_mgr/libsnapshot/snapshot_metadata_updater.h
deleted file mode 100644
index 83c9460..0000000
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater.h
+++ /dev/null
@@ -1,85 +0,0 @@
-//
-// 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>
-#include <vector>
-
-#include <liblp/builder.h>
-#include <update_engine/update_metadata.pb.h>
-
-#include "utility.h"
-
-namespace android {
-namespace snapshot {
-
-// Helper class that modifies a super partition metadata for an update for
-// Virtual A/B devices.
-class SnapshotMetadataUpdater {
-    using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
-    using DynamicPartitionMetadata = chromeos_update_engine::DynamicPartitionMetadata;
-    using DynamicPartitionGroup = chromeos_update_engine::DynamicPartitionGroup;
-    using PartitionUpdate = chromeos_update_engine::PartitionUpdate;
-
-  public:
-    // Caller is responsible for ensuring the lifetime of manifest to be longer
-    // than SnapshotMetadataUpdater.
-    SnapshotMetadataUpdater(android::fs_mgr::MetadataBuilder* builder, uint32_t target_slot,
-                            const DeltaArchiveManifest& manifest);
-    bool Update() const;
-
-  private:
-    bool RenameGroupSuffix() const;
-    bool ShrinkPartitions() const;
-    bool DeletePartitions() const;
-    bool MovePartitionsToDefault() const;
-    bool ShrinkGroups() const;
-    bool DeleteGroups() const;
-    bool AddGroups() const;
-    bool GrowGroups() const;
-    bool AddPartitions() const;
-    bool GrowPartitions() const;
-    bool MovePartitionsToCorrectGroup() const;
-
-    // Wraps a DynamicPartitionGroup with a slot-suffixed name. Always use
-    // .name instead of ->name() because .name has the slot suffix (e.g.
-    // .name is "group_b" and ->name() is "group".)
-    struct Group {
-        std::string name;
-        const DynamicPartitionGroup* group;
-        const DynamicPartitionGroup* operator->() const { return group; }
-    };
-    // Wraps a PartitionUpdate with a slot-suffixed name / group name. Always use
-    // .name instead of ->partition_name() because .name has the slot suffix (e.g.
-    // .name is "system_b" and ->partition_name() is "system".)
-    struct Partition {
-        std::string name;
-        std::string group_name;
-        const PartitionUpdate* partition;
-        const PartitionUpdate* operator->() const { return partition; }
-    };
-
-    android::fs_mgr::MetadataBuilder* const builder_;
-    const std::string target_suffix_;
-    std::vector<Group> groups_;
-    std::vector<Partition> partitions_;
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
deleted file mode 100644
index 4fd8759..0000000
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ /dev/null
@@ -1,331 +0,0 @@
-//
-// 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 "snapshot_metadata_updater.h"
-
-#include <memory>
-#include <string>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <liblp/builder.h>
-#include <storage_literals/storage_literals.h>
-
-#include "test_helpers.h"
-
-using namespace android::storage_literals;
-using android::fs_mgr::LpMetadata;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::SlotSuffixForSlotNumber;
-using chromeos_update_engine::DeltaArchiveManifest;
-using chromeos_update_engine::DynamicPartitionGroup;
-using chromeos_update_engine::PartitionUpdate;
-using testing::AssertionFailure;
-using testing::AssertionResult;
-using testing::AssertionSuccess;
-
-namespace android {
-namespace snapshot {
-
-class SnapshotMetadataUpdaterTest : public ::testing::TestWithParam<uint32_t> {
-  public:
-    void SetUp() override {
-        target_slot_ = GetParam();
-        target_suffix_ = SlotSuffixForSlotNumber(target_slot_);
-        SnapshotTestPropertyFetcher::SetUp(SlotSuffixForSlotNumber(1 - target_slot_));
-        builder_ = MetadataBuilder::New(4_GiB + 1_MiB, 4_KiB, 2);
-
-        group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();
-        group_->set_name("group");
-        group_->set_size(4_GiB);
-        group_->add_partition_names("system");
-        group_->add_partition_names("vendor");
-        system_ = manifest_.add_partitions();
-        system_->set_partition_name("system");
-        SetSize(system_, 2_GiB);
-        vendor_ = manifest_.add_partitions();
-        vendor_->set_partition_name("vendor");
-        SetSize(vendor_, 1_GiB);
-
-        ASSERT_TRUE(FillFakeMetadata(builder_.get(), manifest_, target_suffix_));
-    }
-
-    void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
-
-    // Append suffix to name.
-    std::string T(std::string_view name) { return std::string(name) + target_suffix_; }
-
-    AssertionResult UpdateAndExport() {
-        SnapshotMetadataUpdater updater(builder_.get(), target_slot_, manifest_);
-        if (!updater.Update()) {
-            return AssertionFailure() << "Update failed.";
-        }
-
-        exported_ = builder_->Export();
-        if (exported_ == nullptr) {
-            return AssertionFailure() << "Export failed.";
-        }
-        return AssertionSuccess();
-    }
-
-    // Check that in |builder_|, partition |name| + |target_suffix_| has the given |size|.
-    AssertionResult CheckSize(std::string_view name, uint64_t size) {
-        auto p = builder_->FindPartition(T(name));
-        if (p == nullptr) {
-            return AssertionFailure() << "Cannot find partition " << T(name);
-        }
-        if (p->size() != size) {
-            return AssertionFailure() << "Partition " << T(name) << " should be " << size
-                                      << " bytes, but is " << p->size() << " bytes.";
-        }
-        return AssertionSuccess() << "Partition" << T(name) << " is " << size << " bytes.";
-    }
-
-    // Check that in |builder_|, group |name| + |target_suffix_| has the given |size|.
-    AssertionResult CheckGroupSize(std::string_view name, uint64_t size) {
-        auto g = builder_->FindGroup(T(name));
-        if (g == nullptr) {
-            return AssertionFailure() << "Cannot find group " << T(name);
-        }
-        if (g->maximum_size() != size) {
-            return AssertionFailure() << "Group " << T(name) << " should be " << size
-                                      << " bytes, but is " << g->maximum_size() << " bytes.";
-        }
-        return AssertionSuccess() << "Group" << T(name) << " is " << size << " bytes.";
-    }
-
-    // Check that in |builder_|, partition |partition_name| + |target_suffix_| is in group
-    // |group_name| + |target_suffix_|;
-    AssertionResult CheckGroupName(std::string_view partition_name, std::string_view group_name) {
-        auto p = builder_->FindPartition(T(partition_name));
-        if (p == nullptr) {
-            return AssertionFailure() << "Cannot find partition " << T(partition_name);
-        }
-        if (p->group_name() != T(group_name)) {
-            return AssertionFailure() << "Partition " << T(partition_name) << " should be in "
-                                      << T(group_name) << ", but is in " << p->group_name() << ".";
-        }
-        return AssertionSuccess() << "Partition" << T(partition_name) << " is in " << T(group_name)
-                                  << ".";
-    }
-
-    std::unique_ptr<MetadataBuilder> builder_;
-    uint32_t target_slot_;
-    std::string target_suffix_;
-    DeltaArchiveManifest manifest_;
-    std::unique_ptr<LpMetadata> exported_;
-    DynamicPartitionGroup* group_ = nullptr;
-    PartitionUpdate* system_ = nullptr;
-    PartitionUpdate* vendor_ = nullptr;
-};
-
-TEST_P(SnapshotMetadataUpdaterTest, NoChange) {
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckGroupSize("group", 4_GiB));
-    EXPECT_TRUE(CheckSize("system", 2_GiB));
-    EXPECT_TRUE(CheckGroupName("system", "group"));
-    EXPECT_TRUE(CheckSize("vendor", 1_GiB));
-    EXPECT_TRUE(CheckGroupName("vendor", "group"));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, GrowWithinBounds) {
-    SetSize(system_, 2_GiB + 512_MiB);
-    SetSize(vendor_, 1_GiB + 512_MiB);
-
-    ASSERT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 2_GiB + 512_MiB));
-    EXPECT_TRUE(CheckSize("vendor", 1_GiB + 512_MiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, GrowOverSuper) {
-    SetSize(system_, 3_GiB);
-    SetSize(vendor_, 1_GiB + 512_MiB);
-
-    EXPECT_FALSE(UpdateAndExport());
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, GrowOverGroup) {
-    SetSize(system_, 3_GiB);
-    SetSize(vendor_, 1_GiB + 4_KiB);
-
-    EXPECT_FALSE(UpdateAndExport());
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, Add) {
-    group_->add_partition_names("product");
-    auto product = manifest_.add_partitions();
-    product->set_partition_name("product");
-    SetSize(product, 1_GiB);
-
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 2_GiB));
-    EXPECT_TRUE(CheckSize("vendor", 1_GiB));
-    EXPECT_TRUE(CheckSize("product", 1_GiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, AddTooBig) {
-    group_->add_partition_names("product");
-    auto product = manifest_.add_partitions();
-    product->set_partition_name("product");
-    SetSize(product, 1_GiB + 4_KiB);
-
-    EXPECT_FALSE(UpdateAndExport());
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, ShrinkAll) {
-    SetSize(system_, 1_GiB);
-    SetSize(vendor_, 512_MiB);
-
-    ASSERT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 1_GiB));
-    EXPECT_TRUE(CheckSize("vendor", 512_MiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, ShrinkAndGrow) {
-    SetSize(system_, 3_GiB + 512_MiB);
-    SetSize(vendor_, 512_MiB);
-
-    ASSERT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 3_GiB + 512_MiB));
-    EXPECT_TRUE(CheckSize("vendor", 512_MiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, ShrinkAndAdd) {
-    SetSize(system_, 2_GiB);
-    SetSize(vendor_, 512_MiB);
-    group_->add_partition_names("product");
-    auto product = manifest_.add_partitions();
-    product->set_partition_name("product");
-    SetSize(product, 1_GiB + 512_MiB);
-
-    ASSERT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 2_GiB));
-    EXPECT_TRUE(CheckSize("vendor", 512_MiB));
-    EXPECT_TRUE(CheckSize("product", 1_GiB + 512_MiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, Delete) {
-    group_->mutable_partition_names()->RemoveLast();
-    // No need to delete it from manifest.partitions as SnapshotMetadataUpdater
-    // should ignore them (treat them as static partitions).
-
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 2_GiB));
-    EXPECT_EQ(nullptr, builder_->FindPartition(T("vendor")));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, DeleteAndGrow) {
-    group_->mutable_partition_names()->RemoveLast();
-    SetSize(system_, 4_GiB);
-
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 4_GiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, DeleteAndAdd) {
-    group_->mutable_partition_names()->RemoveLast();
-    group_->add_partition_names("product");
-    auto product = manifest_.add_partitions();
-    product->set_partition_name("product");
-    SetSize(product, 2_GiB);
-
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 2_GiB));
-    EXPECT_EQ(nullptr, builder_->FindPartition(T("vendor")));
-    EXPECT_TRUE(CheckSize("product", 2_GiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, GrowGroup) {
-    group_->set_size(4_GiB + 512_KiB);
-    SetSize(system_, 2_GiB + 256_KiB);
-    SetSize(vendor_, 2_GiB + 256_KiB);
-
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 2_GiB + 256_KiB));
-    EXPECT_TRUE(CheckSize("vendor", 2_GiB + 256_KiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, ShrinkGroup) {
-    group_->set_size(1_GiB);
-    SetSize(system_, 512_MiB);
-    SetSize(vendor_, 512_MiB);
-
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckSize("system", 512_MiB));
-    EXPECT_TRUE(CheckSize("vendor", 512_MiB));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, MoveToNewGroup) {
-    group_->mutable_partition_names()->RemoveLast();
-    group_->set_size(2_GiB);
-
-    auto another_group = manifest_.mutable_dynamic_partition_metadata()->add_groups();
-    another_group->set_name("another_group");
-    another_group->set_size(2_GiB);
-    another_group->add_partition_names("vendor");
-    SetSize(vendor_, 2_GiB);
-
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_TRUE(CheckGroupSize("group", 2_GiB));
-    EXPECT_TRUE(CheckGroupSize("another_group", 2_GiB));
-    EXPECT_TRUE(CheckSize("system", 2_GiB));
-    EXPECT_TRUE(CheckGroupName("system", "group"));
-    EXPECT_TRUE(CheckSize("vendor", 2_GiB));
-    EXPECT_TRUE(CheckGroupName("vendor", "another_group"));
-}
-
-TEST_P(SnapshotMetadataUpdaterTest, DeleteAndAddGroup) {
-    manifest_.mutable_dynamic_partition_metadata()->mutable_groups()->RemoveLast();
-    group_ = nullptr;
-
-    auto another_group = manifest_.mutable_dynamic_partition_metadata()->add_groups();
-    another_group->set_name("another_group");
-    another_group->set_size(4_GiB);
-    another_group->add_partition_names("system");
-    another_group->add_partition_names("vendor");
-    another_group->add_partition_names("product");
-    auto product = manifest_.add_partitions();
-    product->set_partition_name("product");
-    SetSize(product, 1_GiB);
-
-    EXPECT_TRUE(UpdateAndExport());
-
-    EXPECT_EQ(nullptr, builder_->FindGroup(T("group")));
-    EXPECT_TRUE(CheckGroupSize("another_group", 4_GiB));
-    EXPECT_TRUE(CheckSize("system", 2_GiB));
-    EXPECT_TRUE(CheckGroupName("system", "another_group"));
-    EXPECT_TRUE(CheckSize("vendor", 1_GiB));
-    EXPECT_TRUE(CheckGroupName("vendor", "another_group"));
-    EXPECT_TRUE(CheckSize("product", 1_GiB));
-    EXPECT_TRUE(CheckGroupName("product", "another_group"));
-}
-
-INSTANTIATE_TEST_SUITE_P(Snapshot, SnapshotMetadataUpdaterTest, testing::Values(0, 1));
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
deleted file mode 100644
index af268f9..0000000
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ /dev/null
@@ -1,1421 +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 <libsnapshot/snapshot.h>
-
-#include <fcntl.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <chrono>
-#include <iostream>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr/roots.h>
-#include <fs_mgr_dm_linear.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/builder.h>
-#include <storage_literals/storage_literals.h>
-
-#include <android/snapshot/snapshot.pb.h>
-#include "test_helpers.h"
-#include "utility.h"
-
-namespace android {
-namespace snapshot {
-
-using android::base::unique_fd;
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::fiemap::IImageManager;
-using android::fs_mgr::BlockDeviceInfo;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fs_mgr::DestroyLogicalPartition;
-using android::fs_mgr::EnsurePathMounted;
-using android::fs_mgr::EnsurePathUnmounted;
-using android::fs_mgr::Extent;
-using android::fs_mgr::Fstab;
-using android::fs_mgr::GetPartitionGroupName;
-using android::fs_mgr::GetPartitionName;
-using android::fs_mgr::Interval;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::SlotSuffixForSlotNumber;
-using chromeos_update_engine::DeltaArchiveManifest;
-using chromeos_update_engine::DynamicPartitionGroup;
-using chromeos_update_engine::PartitionUpdate;
-using namespace ::testing;
-using namespace android::storage_literals;
-using namespace std::chrono_literals;
-using namespace std::string_literals;
-
-// Global states. See test_helpers.h.
-std::unique_ptr<SnapshotManager> sm;
-TestDeviceInfo* test_device = nullptr;
-std::string fake_super;
-
-class SnapshotTest : public ::testing::Test {
-  public:
-    SnapshotTest() : dm_(DeviceMapper::Instance()) {}
-
-    // This is exposed for main.
-    void Cleanup() {
-        InitializeState();
-        CleanupTestArtifacts();
-    }
-
-  protected:
-    void SetUp() override {
-        SnapshotTestPropertyFetcher::SetUp();
-        InitializeState();
-        CleanupTestArtifacts();
-        FormatFakeSuper();
-
-        ASSERT_TRUE(sm->BeginUpdate());
-    }
-
-    void TearDown() override {
-        lock_ = nullptr;
-
-        CleanupTestArtifacts();
-        SnapshotTestPropertyFetcher::TearDown();
-    }
-
-    void InitializeState() {
-        ASSERT_TRUE(sm->EnsureImageManager());
-        image_manager_ = sm->image_manager();
-
-        test_device->set_slot_suffix("_a");
-    }
-
-    void CleanupTestArtifacts() {
-        // Normally cancelling inside a merge is not allowed. Since these
-        // are tests, we don't care, destroy everything that might exist.
-        // Note we hardcode this list because of an annoying quirk: when
-        // completing a merge, the snapshot stops existing, so we can't
-        // get an accurate list to remove.
-        lock_ = nullptr;
-
-        std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
-                                              "test_partition_b"};
-        for (const auto& snapshot : snapshots) {
-            ASSERT_TRUE(DeleteSnapshotDevice(snapshot));
-            DeleteBackingImage(image_manager_, snapshot + "-cow-img");
-
-            auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
-            android::base::RemoveFileIfExists(status_file);
-        }
-
-        // Remove stale partitions in fake super.
-        std::vector<std::string> partitions = {
-                "base-device",
-                "test_partition_b",
-                "test_partition_b-base",
-        };
-        for (const auto& partition : partitions) {
-            DeleteDevice(partition);
-        }
-
-        if (sm->GetUpdateState() != UpdateState::None) {
-            auto state_file = sm->GetStateFilePath();
-            unlink(state_file.c_str());
-        }
-    }
-
-    bool AcquireLock() {
-        lock_ = sm->OpenStateFile(O_RDWR, LOCK_EX);
-        return !!lock_;
-    }
-
-    // This is so main() can instantiate this to invoke Cleanup.
-    virtual void TestBody() override {}
-
-    void FormatFakeSuper() {
-        BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4096);
-        std::vector<BlockDeviceInfo> devices = {super_device};
-
-        auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
-        ASSERT_NE(builder, nullptr);
-
-        auto metadata = builder->Export();
-        ASSERT_NE(metadata, nullptr);
-
-        TestPartitionOpener opener(fake_super);
-        ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *metadata.get()));
-    }
-
-    // If |path| is non-null, the partition will be mapped after creation.
-    bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) {
-        TestPartitionOpener opener(fake_super);
-        auto builder = MetadataBuilder::New(opener, "super", 0);
-        if (!builder) return false;
-
-        auto partition = builder->AddPartition(name, 0);
-        if (!partition) return false;
-        if (!builder->ResizePartition(partition, size)) {
-            return false;
-        }
-
-        // Update the source slot.
-        auto metadata = builder->Export();
-        if (!metadata) return false;
-        if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
-            return false;
-        }
-
-        if (!path) return true;
-
-        CreateLogicalPartitionParams params = {
-                .block_device = fake_super,
-                .metadata = metadata.get(),
-                .partition_name = name,
-                .force_writable = true,
-                .timeout_ms = 10s,
-        };
-        return CreateLogicalPartition(params, path);
-    }
-
-    bool MapUpdatePartitions() {
-        TestPartitionOpener opener(fake_super);
-        auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
-        if (!builder) return false;
-
-        auto metadata = builder->Export();
-        if (!metadata) return false;
-
-        // Update the destination slot, mark it as updated.
-        if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
-            return false;
-        }
-
-        for (const auto& partition : metadata->partitions) {
-            CreateLogicalPartitionParams params = {
-                    .block_device = fake_super,
-                    .metadata = metadata.get(),
-                    .partition = &partition,
-                    .force_writable = true,
-                    .timeout_ms = 10s,
-                    .device_name = GetPartitionName(partition) + "-base",
-            };
-            std::string ignore_path;
-            if (!CreateLogicalPartition(params, &ignore_path)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    AssertionResult DeleteSnapshotDevice(const std::string& snapshot) {
-        AssertionResult res = AssertionSuccess();
-        if (!(res = DeleteDevice(snapshot))) return res;
-        if (!(res = DeleteDevice(snapshot + "-inner"))) return res;
-        if (!(res = DeleteDevice(snapshot + "-cow"))) return res;
-        if (!image_manager_->UnmapImageIfExists(snapshot + "-cow-img")) {
-            return AssertionFailure() << "Cannot unmap image " << snapshot << "-cow-img";
-        }
-        if (!(res = DeleteDevice(snapshot + "-base"))) return res;
-        return AssertionSuccess();
-    }
-
-    AssertionResult DeleteDevice(const std::string& device) {
-        if (!dm_.DeleteDeviceIfExists(device)) {
-            return AssertionFailure() << "Can't delete " << device;
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult CreateCowImage(const std::string& name) {
-        if (!sm->CreateCowImage(lock_.get(), name)) {
-            return AssertionFailure() << "Cannot create COW image " << name;
-        }
-        std::string cow_device;
-        auto map_res = MapCowImage(name, 10s, &cow_device);
-        if (!map_res) {
-            return map_res;
-        }
-        if (!InitializeCow(cow_device)) {
-            return AssertionFailure() << "Cannot zero fill " << cow_device;
-        }
-        if (!sm->UnmapCowImage(name)) {
-            return AssertionFailure() << "Cannot unmap " << name << " after zero filling it";
-        }
-        return AssertionSuccess();
-    }
-
-    AssertionResult MapCowImage(const std::string& name,
-                                const std::chrono::milliseconds& timeout_ms, std::string* path) {
-        auto cow_image_path = sm->MapCowImage(name, timeout_ms);
-        if (!cow_image_path.has_value()) {
-            return AssertionFailure() << "Cannot map cow image " << name;
-        }
-        *path = *cow_image_path;
-        return AssertionSuccess();
-    }
-
-    DeviceMapper& dm_;
-    std::unique_ptr<SnapshotManager::LockedFile> lock_;
-    android::fiemap::IImageManager* image_manager_ = nullptr;
-    std::string fake_super_;
-};
-
-TEST_F(SnapshotTest, CreateSnapshot) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-    SnapshotStatus status;
-    status.set_name("test-snapshot");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
-    std::vector<std::string> snapshots;
-    ASSERT_TRUE(sm->ListSnapshots(lock_.get(), &snapshots));
-    ASSERT_EQ(snapshots.size(), 1);
-    ASSERT_EQ(snapshots[0], "test-snapshot");
-
-    // Scope so delete can re-acquire the snapshot file lock.
-    {
-        SnapshotStatus status;
-        ASSERT_TRUE(sm->ReadSnapshotStatus(lock_.get(), "test-snapshot", &status));
-        ASSERT_EQ(status.state(), SnapshotState::CREATED);
-        ASSERT_EQ(status.device_size(), kDeviceSize);
-        ASSERT_EQ(status.snapshot_size(), kDeviceSize);
-    }
-
-    ASSERT_TRUE(sm->UnmapSnapshot(lock_.get(), "test-snapshot"));
-    ASSERT_TRUE(sm->UnmapCowImage("test-snapshot"));
-    ASSERT_TRUE(sm->DeleteSnapshot(lock_.get(), "test-snapshot"));
-}
-
-TEST_F(SnapshotTest, MapSnapshot) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-    SnapshotStatus status;
-    status.set_name("test-snapshot");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
-    std::string base_device;
-    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-
-    std::string cow_device;
-    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
-
-    std::string snap_device;
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
-                                &snap_device));
-    ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
-}
-
-TEST_F(SnapshotTest, MapPartialSnapshot) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kSnapshotSize = 1024 * 1024;
-    static const uint64_t kDeviceSize = 1024 * 1024 * 2;
-    SnapshotStatus status;
-    status.set_name("test-snapshot");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kSnapshotSize);
-    status.set_cow_file_size(kSnapshotSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test-snapshot"));
-
-    std::string base_device;
-    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-
-    std::string cow_device;
-    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
-
-    std::string snap_device;
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
-                                &snap_device));
-    ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
-}
-
-TEST_F(SnapshotTest, NoMergeBeforeReboot) {
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    // Merge should fail, since the slot hasn't changed.
-    ASSERT_FALSE(sm->InitiateMerge());
-}
-
-TEST_F(SnapshotTest, CleanFirstStageMount) {
-    // If there's no update in progress, there should be no first-stage mount
-    // needed.
-    TestDeviceInfo* info = new TestDeviceInfo(fake_super);
-    auto sm = SnapshotManager::NewForFirstStageMount(info);
-    ASSERT_NE(sm, nullptr);
-    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
-}
-
-TEST_F(SnapshotTest, FirstStageMountAfterRollback) {
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    // We didn't change the slot, so we shouldn't need snapshots.
-    TestDeviceInfo* info = new TestDeviceInfo(fake_super);
-    auto sm = SnapshotManager::NewForFirstStageMount(info);
-    ASSERT_NE(sm, nullptr);
-    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
-}
-
-TEST_F(SnapshotTest, Merge) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-
-    std::string base_device, cow_device, snap_device;
-    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
-    ASSERT_TRUE(MapUpdatePartitions());
-    ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b-base", &base_device));
-    SnapshotStatus status;
-    status.set_name("test_partition_b");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test_partition_b"));
-    ASSERT_TRUE(MapCowImage("test_partition_b", 10s, &cow_device));
-    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
-                                &snap_device));
-
-    std::string test_string = "This is a test string.";
-    {
-        unique_fd fd(open(snap_device.c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
-        ASSERT_GE(fd, 0);
-        ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
-    }
-
-    // Note: we know there is no inner/outer dm device since we didn't request
-    // a linear segment.
-    DeviceMapper::TargetInfo target;
-    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
-
-    // Release the lock.
-    lock_ = nullptr;
-
-    // Done updating.
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    test_device->set_slot_suffix("_b");
-    ASSERT_TRUE(sm->InitiateMerge());
-
-    // The device should have been switched to a snapshot-merge target.
-    ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
-
-    // We should not be able to cancel an update now.
-    ASSERT_FALSE(sm->CancelUpdate());
-
-    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
-    ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
-
-    // The device should no longer be a snapshot or snapshot-merge.
-    ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
-
-    // Test that we can read back the string we wrote to the snapshot. Note
-    // that the base device is gone now. |snap_device| contains the correct
-    // partition.
-    unique_fd fd(open(snap_device.c_str(), O_RDONLY | O_CLOEXEC));
-    ASSERT_GE(fd, 0);
-
-    std::string buffer(test_string.size(), '\0');
-    ASSERT_TRUE(android::base::ReadFully(fd, buffer.data(), buffer.size()));
-    ASSERT_EQ(test_string, buffer);
-}
-
-TEST_F(SnapshotTest, FirstStageMountAndMerge) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-
-    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
-    ASSERT_TRUE(MapUpdatePartitions());
-    SnapshotStatus status;
-    status.set_name("test_partition_b");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
-    // Simulate a reboot into the new slot.
-    lock_ = nullptr;
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
-
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-
-    ASSERT_TRUE(AcquireLock());
-
-    // Validate that we have a snapshot device.
-    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
-    ASSERT_EQ(status.state(), SnapshotState::CREATED);
-
-    DeviceMapper::TargetInfo target;
-    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
-    ASSERT_TRUE(init->IsSnapshotDevice(dm_name, &target));
-    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
-}
-
-TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-
-    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
-    ASSERT_TRUE(MapUpdatePartitions());
-    SnapshotStatus status;
-    status.set_name("test_partition_b");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
-    // Simulate a reboot into the new slot.
-    lock_ = nullptr;
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
-
-    // Reflash the super partition.
-    FormatFakeSuper();
-    ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
-
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-
-    ASSERT_TRUE(AcquireLock());
-
-    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
-
-    // We should not get a snapshot device now.
-    DeviceMapper::TargetInfo target;
-    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
-    ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target));
-
-    // We should see a cancelled update as well.
-    lock_ = nullptr;
-    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
-}
-
-TEST_F(SnapshotTest, FlashSuperDuringMerge) {
-    ASSERT_TRUE(AcquireLock());
-
-    static const uint64_t kDeviceSize = 1024 * 1024;
-
-    ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
-    ASSERT_TRUE(MapUpdatePartitions());
-    SnapshotStatus status;
-    status.set_name("test_partition_b");
-    status.set_device_size(kDeviceSize);
-    status.set_snapshot_size(kDeviceSize);
-    status.set_cow_file_size(kDeviceSize);
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &status));
-    ASSERT_TRUE(CreateCowImage("test_partition_b"));
-
-    // Simulate a reboot into the new slot.
-    lock_ = nullptr;
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
-
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-    ASSERT_TRUE(init->InitiateMerge());
-
-    // Now, reflash super. Note that we haven't called ProcessUpdateState, so the
-    // status is still Merging.
-    ASSERT_TRUE(DeleteSnapshotDevice("test_partition_b"));
-    ASSERT_TRUE(init->image_manager()->UnmapImageIfExists("test_partition_b-cow-img"));
-    FormatFakeSuper();
-    ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-
-    // Because the status is Merging, we must call ProcessUpdateState, which should
-    // detect a cancelled update.
-    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
-    ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
-}
-
-TEST_F(SnapshotTest, UpdateBootControlHal) {
-    ASSERT_TRUE(AcquireLock());
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Initiated));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Merging));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeNeedsReboot));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeCompleted));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
-
-    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeFailed));
-    ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);
-}
-
-class SnapshotUpdateTest : public SnapshotTest {
-  public:
-    void SetUp() override {
-        SnapshotTest::SetUp();
-        Cleanup();
-
-        // Cleanup() changes slot suffix, so initialize it again.
-        test_device->set_slot_suffix("_a");
-
-        opener_ = std::make_unique<TestPartitionOpener>(fake_super);
-
-        // Create a fake update package metadata.
-        // Not using full name "system", "vendor", "product" because these names collide with the
-        // mapped partitions on the running device.
-        // Each test modifies manifest_ slightly to indicate changes to the partition layout.
-        group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();
-        group_->set_name("group");
-        group_->set_size(kGroupSize);
-        group_->add_partition_names("sys");
-        group_->add_partition_names("vnd");
-        group_->add_partition_names("prd");
-        sys_ = manifest_.add_partitions();
-        sys_->set_partition_name("sys");
-        SetSize(sys_, 3_MiB);
-        vnd_ = manifest_.add_partitions();
-        vnd_->set_partition_name("vnd");
-        SetSize(vnd_, 3_MiB);
-        prd_ = manifest_.add_partitions();
-        prd_->set_partition_name("prd");
-        SetSize(prd_, 3_MiB);
-
-        // Initialize source partition metadata using |manifest_|.
-        src_ = MetadataBuilder::New(*opener_, "super", 0);
-        ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
-        // Add sys_b which is like system_other.
-        ASSERT_TRUE(src_->AddGroup("group_b", kGroupSize));
-        auto partition = src_->AddPartition("sys_b", "group_b", 0);
-        ASSERT_NE(nullptr, partition);
-        ASSERT_TRUE(src_->ResizePartition(partition, 1_MiB));
-        auto metadata = src_->Export();
-        ASSERT_NE(nullptr, metadata);
-        ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
-
-        // Map source partitions. Additionally, map sys_b to simulate system_other after flashing.
-        std::string path;
-        for (const auto& name : {"sys_a", "vnd_a", "prd_a", "sys_b"}) {
-            ASSERT_TRUE(CreateLogicalPartition(
-                    CreateLogicalPartitionParams{
-                            .block_device = fake_super,
-                            .metadata_slot = 0,
-                            .partition_name = name,
-                            .timeout_ms = 1s,
-                            .partition_opener = opener_.get(),
-                    },
-                    &path));
-            ASSERT_TRUE(WriteRandomData(path));
-            auto hash = GetHash(path);
-            ASSERT_TRUE(hash.has_value());
-            hashes_[name] = *hash;
-        }
-    }
-    void TearDown() override {
-        Cleanup();
-        SnapshotTest::TearDown();
-    }
-    void Cleanup() {
-        if (!image_manager_) {
-            InitializeState();
-        }
-        for (const auto& suffix : {"_a", "_b"}) {
-            test_device->set_slot_suffix(suffix);
-            EXPECT_TRUE(sm->CancelUpdate()) << suffix;
-        }
-        EXPECT_TRUE(UnmapAll());
-    }
-
-    AssertionResult IsPartitionUnchanged(const std::string& name) {
-        std::string path;
-        if (!dm_.GetDmDevicePathByName(name, &path)) {
-            return AssertionFailure() << "Path of " << name << " cannot be determined";
-        }
-        auto hash = GetHash(path);
-        if (!hash.has_value()) {
-            return AssertionFailure() << "Cannot read partition " << name << ": " << path;
-        }
-        auto it = hashes_.find(name);
-        if (it == hashes_.end()) {
-            return AssertionFailure() << "No existing hash for " << name << ". Bad test code?";
-        }
-        if (it->second != *hash) {
-            return AssertionFailure() << "Content of " << name << " has changed";
-        }
-        return AssertionSuccess();
-    }
-
-    std::optional<uint64_t> GetSnapshotSize(const std::string& name) {
-        if (!AcquireLock()) {
-            return std::nullopt;
-        }
-        auto local_lock = std::move(lock_);
-
-        SnapshotStatus status;
-        if (!sm->ReadSnapshotStatus(local_lock.get(), name, &status)) {
-            return std::nullopt;
-        }
-        return status.snapshot_size();
-    }
-
-    AssertionResult UnmapAll() {
-        for (const auto& name : {"sys", "vnd", "prd"}) {
-            if (!dm_.DeleteDeviceIfExists(name + "_a"s)) {
-                return AssertionFailure() << "Cannot unmap " << name << "_a";
-            }
-            if (!DeleteSnapshotDevice(name + "_b"s)) {
-                return AssertionFailure() << "Cannot delete snapshot " << name << "_b";
-            }
-        }
-        return AssertionSuccess();
-    }
-
-    std::unique_ptr<TestPartitionOpener> opener_;
-    DeltaArchiveManifest manifest_;
-    std::unique_ptr<MetadataBuilder> src_;
-    std::map<std::string, std::string> hashes_;
-
-    PartitionUpdate* sys_ = nullptr;
-    PartitionUpdate* vnd_ = nullptr;
-    PartitionUpdate* prd_ = nullptr;
-    DynamicPartitionGroup* group_ = nullptr;
-};
-
-// Test full update flow executed by update_engine. Some partitions uses super empty space,
-// some uses images, and some uses both.
-// Also test UnmapUpdateSnapshot unmaps everything.
-// Also test first stage mount and merge after this.
-TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
-    // Grow all partitions.
-    constexpr uint64_t partition_size = 3788_KiB;
-    SetSize(sys_, partition_size);
-    SetSize(vnd_, partition_size);
-    SetSize(prd_, partition_size);
-
-    // Create fake install operations to grow the COW device size.
-    for (auto& partition : {sys_, vnd_, prd_}) {
-        auto e = partition->add_operations()->add_dst_extents();
-        e->set_start_block(0);
-        e->set_num_blocks(GetSize(partition) / manifest_.block_size());
-    }
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Test that partitions prioritize using space in super.
-    auto tgt = MetadataBuilder::New(*opener_, "super", 1);
-    ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
-    ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
-    ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        std::string path;
-        ASSERT_TRUE(sm->MapUpdateSnapshot(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 1,
-                        .partition_name = name,
-                        .timeout_ms = 10s,
-                        .partition_opener = opener_.get(),
-                },
-                &path))
-                << name;
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name] = *hash;
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
-
-    // Check that the target partitions have the same content after the merge.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name))
-                << "Content of " << name << " changes after the merge";
-    }
-}
-
-// Test that if new system partitions uses empty space in super, that region is not snapshotted.
-TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
-    GTEST_SKIP() << "b/141889746";
-    SetSize(sys_, 4_MiB);
-    // vnd_b and prd_b are unchanged.
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_EQ(3_MiB, GetSnapshotSize("sys_b").value_or(0));
-}
-
-// Test that if new system partitions uses space of old vendor partition, that region is
-// snapshotted.
-TEST_F(SnapshotUpdateTest, SnapshotOldPartitions) {
-    SetSize(sys_, 4_MiB);  // grows
-    SetSize(vnd_, 2_MiB);  // shrinks
-    // prd_b is unchanged
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_EQ(4_MiB, GetSnapshotSize("sys_b").value_or(0));
-}
-
-// Test that even if there seem to be empty space in target metadata, COW partition won't take
-// it because they are used by old partitions.
-TEST_F(SnapshotUpdateTest, CowPartitionDoNotTakeOldPartitions) {
-    SetSize(sys_, 2_MiB);  // shrinks
-    // vnd_b and prd_b are unchanged.
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    auto tgt = MetadataBuilder::New(*opener_, "super", 1);
-    ASSERT_NE(nullptr, tgt);
-    auto metadata = tgt->Export();
-    ASSERT_NE(nullptr, metadata);
-    std::vector<std::string> written;
-    // Write random data to all COW partitions in super
-    for (auto p : metadata->partitions) {
-        if (GetPartitionGroupName(metadata->groups[p.group_index]) != kCowGroupName) {
-            continue;
-        }
-        std::string path;
-        ASSERT_TRUE(CreateLogicalPartition(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata = metadata.get(),
-                        .partition = &p,
-                        .timeout_ms = 1s,
-                        .partition_opener = opener_.get(),
-                },
-                &path));
-        ASSERT_TRUE(WriteRandomData(path));
-        written.push_back(GetPartitionName(p));
-    }
-    ASSERT_FALSE(written.empty())
-            << "No COW partitions are created even if there are empty space in super partition";
-
-    // Make sure source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-}
-
-// Test that it crashes after creating snapshot status file but before creating COW image, then
-// calling CreateUpdateSnapshots again works.
-TEST_F(SnapshotUpdateTest, SnapshotStatusFileWithoutCow) {
-    // Write some trash snapshot files to simulate leftovers from previous runs.
-    {
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-        SnapshotStatus status;
-        status.set_name("sys_b");
-        ASSERT_TRUE(sm->WriteSnapshotStatus(local_lock.get(), status));
-        ASSERT_TRUE(image_manager_->CreateBackingImage("sys_b-cow-img", 1_MiB,
-                                                       IImageManager::CREATE_IMAGE_DEFAULT));
-    }
-
-    // Redo the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
-
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Check that target partitions can be mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        std::string path;
-        EXPECT_TRUE(sm->MapUpdateSnapshot(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 1,
-                        .partition_name = name,
-                        .timeout_ms = 10s,
-                        .partition_opener = opener_.get(),
-                },
-                &path))
-                << name;
-    }
-}
-
-// Test that the old partitions are not modified.
-TEST_F(SnapshotUpdateTest, TestRollback) {
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
-
-    // Create fake install operations to grow the COW device size.
-    for (auto& partition : {sys_, vnd_, prd_}) {
-        auto e = partition->add_operations()->add_dst_extents();
-        e->set_start_block(0);
-        e->set_num_blocks(GetSize(partition) / manifest_.block_size());
-    }
-
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        std::string path;
-        ASSERT_TRUE(sm->MapUpdateSnapshot(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 1,
-                        .partition_name = name,
-                        .timeout_ms = 10s,
-                        .partition_opener = opener_.get(),
-                },
-                &path))
-                << name;
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name] = *hash;
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    // Simulate shutting down the device again.
-    ASSERT_TRUE(UnmapAll());
-    init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_a"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-
-    // Assert that the source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-}
-
-// Test that if an update is applied but not booted into, it can be canceled.
-TEST_F(SnapshotUpdateTest, CancelAfterApply) {
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-    ASSERT_TRUE(sm->CancelUpdate());
-}
-
-static std::vector<Interval> ToIntervals(const std::vector<std::unique_ptr<Extent>>& extents) {
-    std::vector<Interval> ret;
-    std::transform(extents.begin(), extents.end(), std::back_inserter(ret),
-                   [](const auto& extent) { return extent->AsLinearExtent()->AsInterval(); });
-    return ret;
-}
-
-// Test that at the second update, old COW partition spaces are reclaimed.
-TEST_F(SnapshotUpdateTest, ReclaimCow) {
-    // Execute the first update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-    init = nullptr;
-
-    // Initiate the merge and wait for it to be completed.
-    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->InitiateMergeAndWait());
-
-    // Execute the second update.
-    ASSERT_TRUE(new_sm->BeginUpdate());
-    ASSERT_TRUE(new_sm->CreateUpdateSnapshots(manifest_));
-
-    // Check that the old COW space is reclaimed and does not occupy space of mapped partitions.
-    auto src = MetadataBuilder::New(*opener_, "super", 1);
-    auto tgt = MetadataBuilder::New(*opener_, "super", 0);
-    for (const auto& cow_part_name : {"sys_a-cow", "vnd_a-cow", "prd_a-cow"}) {
-        auto* cow_part = tgt->FindPartition(cow_part_name);
-        ASSERT_NE(nullptr, cow_part) << cow_part_name << " does not exist in target metadata";
-        auto cow_intervals = ToIntervals(cow_part->extents());
-        for (const auto& old_part_name : {"sys_b", "vnd_b", "prd_b"}) {
-            auto* old_part = src->FindPartition(old_part_name);
-            ASSERT_NE(nullptr, old_part) << old_part_name << " does not exist in source metadata";
-            auto old_intervals = ToIntervals(old_part->extents());
-
-            auto intersect = Interval::Intersect(cow_intervals, old_intervals);
-            ASSERT_TRUE(intersect.empty()) << "COW uses space of source partitions";
-        }
-    }
-}
-
-TEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {
-    constexpr auto kRetrofitGroupSize = kGroupSize / 2;
-
-    // Initialize device-mapper / disk
-    ASSERT_TRUE(UnmapAll());
-    FormatFakeSuper();
-
-    // Setup source partition metadata to have both _a and _b partitions.
-    src_ = MetadataBuilder::New(*opener_, "super", 0);
-    ASSERT_NE(nullptr, src_);
-    for (const auto& suffix : {"_a"s, "_b"s}) {
-        ASSERT_TRUE(src_->AddGroup(group_->name() + suffix, kRetrofitGroupSize));
-        for (const auto& name : {"sys"s, "vnd"s, "prd"s}) {
-            auto partition = src_->AddPartition(name + suffix, group_->name() + suffix, 0);
-            ASSERT_NE(nullptr, partition);
-            ASSERT_TRUE(src_->ResizePartition(partition, 2_MiB));
-        }
-    }
-    auto metadata = src_->Export();
-    ASSERT_NE(nullptr, metadata);
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
-
-    // Flash source partitions
-    std::string path;
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(CreateLogicalPartition(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 0,
-                        .partition_name = name,
-                        .timeout_ms = 1s,
-                        .partition_opener = opener_.get(),
-                },
-                &path));
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name] = *hash;
-    }
-
-    // Setup manifest.
-    group_->set_size(kRetrofitGroupSize);
-    for (auto* partition : {sys_, vnd_, prd_}) {
-        SetSize(partition, 2_MiB);
-        auto* e = partition->add_operations()->add_dst_extents();
-        e->set_start_block(0);
-        e->set_num_blocks(2_MiB / manifest_.block_size());
-    }
-
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    // Test that COW image should not be created for retrofit devices; super
-    // should be big enough.
-    ASSERT_FALSE(image_manager_->BackingImageExists("sys_b-cow-img"));
-    ASSERT_FALSE(image_manager_->BackingImageExists("vnd_b-cow-img"));
-    ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        std::string path;
-        ASSERT_TRUE(sm->MapUpdateSnapshot(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = 1,
-                        .partition_name = name,
-                        .timeout_ms = 10s,
-                        .partition_opener = opener_.get(),
-                },
-                &path))
-                << name;
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name] = *hash;
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-}
-
-TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
-    // Make source partitions as big as possible to force COW image to be created.
-    SetSize(sys_, 5_MiB);
-    SetSize(vnd_, 5_MiB);
-    SetSize(prd_, 5_MiB);
-    src_ = MetadataBuilder::New(*opener_, "super", 0);
-    src_->RemoveGroupAndPartitions(group_->name() + "_a");
-    src_->RemoveGroupAndPartitions(group_->name() + "_b");
-    ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, "_a"));
-    auto metadata = src_->Export();
-    ASSERT_NE(nullptr, metadata);
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
-
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
-    // Add operations for sys. The whole device is written.
-    auto e = sys_->add_operations()->add_dst_extents();
-    e->set_start_block(0);
-    e->set_num_blocks(GetSize(sys_) / manifest_.block_size());
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    // Normally we should use NewForFirstStageMount, but if so, "gsid.mapped_image.sys_b-cow-img"
-    // won't be set.
-    auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-
-    // Keep an open handle to the cow device. This should cause the merge to
-    // be incomplete.
-    auto cow_path = android::base::GetProperty("gsid.mapped_image.sys_b-cow-img", "");
-    unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
-    ASSERT_GE(fd, 0);
-
-    // COW cannot be removed due to open fd, so expect a soft failure.
-    ASSERT_EQ(UpdateState::MergeNeedsReboot, init->InitiateMergeAndWait());
-
-    // Simulate shutting down the device.
-    fd.reset();
-    ASSERT_TRUE(UnmapAll());
-
-    // init does first stage mount again.
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-
-    // sys_b should be mapped as a dm-linear device directly.
-    ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr));
-
-    // Merge should be able to complete now.
-    ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait());
-}
-
-class MetadataMountedTest : public SnapshotUpdateTest {
-  public:
-    void SetUp() override {
-        metadata_dir_ = test_device->GetMetadataDir();
-        ASSERT_TRUE(ReadDefaultFstab(&fstab_));
-    }
-    void TearDown() override {
-        SetUp();
-        // Remount /metadata
-        test_device->set_recovery(false);
-        EXPECT_TRUE(android::fs_mgr::EnsurePathMounted(&fstab_, metadata_dir_));
-    }
-    AssertionResult IsMetadataMounted() {
-        Fstab mounted_fstab;
-        if (!ReadFstabFromFile("/proc/mounts", &mounted_fstab)) {
-            ADD_FAILURE() << "Failed to scan mounted volumes";
-            return AssertionFailure() << "Failed to scan mounted volumes";
-        }
-
-        auto entry = GetEntryForPath(&fstab_, metadata_dir_);
-        if (entry == nullptr) {
-            return AssertionFailure() << "No mount point found in fstab for path " << metadata_dir_;
-        }
-
-        auto mv = GetEntryForMountPoint(&mounted_fstab, entry->mount_point);
-        if (mv == nullptr) {
-            return AssertionFailure() << metadata_dir_ << " is not mounted";
-        }
-        return AssertionSuccess() << metadata_dir_ << " is mounted";
-    }
-    std::string metadata_dir_;
-    Fstab fstab_;
-};
-
-TEST_F(MetadataMountedTest, Android) {
-    auto device = sm->EnsureMetadataMounted();
-    EXPECT_NE(nullptr, device);
-    device.reset();
-
-    EXPECT_TRUE(IsMetadataMounted());
-    EXPECT_TRUE(sm->CancelUpdate()) << "Metadata dir should never be unmounted in Android mode";
-}
-
-TEST_F(MetadataMountedTest, Recovery) {
-    test_device->set_recovery(true);
-    metadata_dir_ = test_device->GetMetadataDir();
-
-    EXPECT_TRUE(android::fs_mgr::EnsurePathUnmounted(&fstab_, metadata_dir_));
-    EXPECT_FALSE(IsMetadataMounted());
-
-    auto device = sm->EnsureMetadataMounted();
-    EXPECT_NE(nullptr, device);
-    EXPECT_TRUE(IsMetadataMounted());
-
-    device.reset();
-    EXPECT_FALSE(IsMetadataMounted());
-}
-
-class FlashAfterUpdateTest : public SnapshotUpdateTest,
-                             public WithParamInterface<std::tuple<uint32_t, bool>> {
-  public:
-    AssertionResult InitiateMerge(const std::string& slot_suffix) {
-        auto sm = SnapshotManager::New(new TestDeviceInfo(fake_super, slot_suffix));
-        if (!sm->CreateLogicalAndSnapshotPartitions("super")) {
-            return AssertionFailure() << "Cannot CreateLogicalAndSnapshotPartitions";
-        }
-        if (!sm->InitiateMerge()) {
-            return AssertionFailure() << "Cannot initiate merge";
-        }
-        return AssertionSuccess();
-    }
-};
-
-TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
-    // OTA client blindly unmaps all partitions that are possibly mapped.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
-    }
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites());
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    if (std::get<1>(GetParam()) /* merge */) {
-        ASSERT_TRUE(InitiateMerge("_b"));
-        // Simulate shutting down the device after merge has initiated.
-        ASSERT_TRUE(UnmapAll());
-    }
-
-    auto flashed_slot = std::get<0>(GetParam());
-    auto flashed_slot_suffix = SlotSuffixForSlotNumber(flashed_slot);
-
-    // Simulate flashing |flashed_slot|. This clears the UPDATED flag.
-    auto flashed_builder = MetadataBuilder::New(*opener_, "super", flashed_slot);
-    flashed_builder->RemoveGroupAndPartitions(group_->name() + flashed_slot_suffix);
-    flashed_builder->RemoveGroupAndPartitions(kCowGroupName);
-    ASSERT_TRUE(FillFakeMetadata(flashed_builder.get(), manifest_, flashed_slot_suffix));
-
-    // Deliberately remove a partition from this build so that
-    // InitiateMerge do not switch state to "merging". This is possible in
-    // practice because the list of dynamic partitions may change.
-    ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix));
-    flashed_builder->RemovePartition("prd" + flashed_slot_suffix);
-
-    auto flashed_metadata = flashed_builder->Export();
-    ASSERT_NE(nullptr, flashed_metadata);
-    ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, flashed_slot));
-
-    std::string path;
-    for (const auto& name : {"sys", "vnd"}) {
-        ASSERT_TRUE(CreateLogicalPartition(
-                CreateLogicalPartitionParams{
-                        .block_device = fake_super,
-                        .metadata_slot = flashed_slot,
-                        .partition_name = name + flashed_slot_suffix,
-                        .timeout_ms = 1s,
-                        .partition_opener = opener_.get(),
-                },
-                &path));
-        ASSERT_TRUE(WriteRandomData(path));
-        auto hash = GetHash(path);
-        ASSERT_TRUE(hash.has_value());
-        hashes_[name + flashed_slot_suffix] = *hash;
-    }
-
-    // Simulate shutting down the device after flash.
-    ASSERT_TRUE(UnmapAll());
-
-    // Simulate reboot. After reboot, init does first stage mount.
-    auto init = SnapshotManager::NewForFirstStageMount(
-            new TestDeviceInfo(fake_super, flashed_slot_suffix));
-    ASSERT_NE(init, nullptr);
-    if (init->NeedSnapshotsInFirstStageMount()) {
-        ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
-    } else {
-        for (const auto& name : {"sys", "vnd"}) {
-            ASSERT_TRUE(CreateLogicalPartition(
-                    CreateLogicalPartitionParams{
-                            .block_device = fake_super,
-                            .metadata_slot = flashed_slot,
-                            .partition_name = name + flashed_slot_suffix,
-                            .timeout_ms = 1s,
-                            .partition_opener = opener_.get(),
-                    },
-                    &path));
-        }
-    }
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys", "vnd"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name + flashed_slot_suffix));
-    }
-
-    // There should be no snapshot to merge.
-    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
-    ASSERT_EQ(UpdateState::Cancelled, new_sm->InitiateMergeAndWait());
-
-    // Next OTA calls CancelUpdate no matter what.
-    ASSERT_TRUE(new_sm->CancelUpdate());
-}
-
-INSTANTIATE_TEST_SUITE_P(Snapshot, FlashAfterUpdateTest, Combine(Values(0, 1), Bool()),
-                         [](const TestParamInfo<FlashAfterUpdateTest::ParamType>& info) {
-                             return "Flash"s + (std::get<0>(info.param) ? "New"s : "Old"s) +
-                                    "Slot"s + (std::get<1>(info.param) ? "After"s : "Before"s) +
-                                    "Merge"s;
-                         });
-
-}  // namespace snapshot
-}  // namespace android
-
-using namespace android::snapshot;
-
-bool Mkdir(const std::string& path) {
-    if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
-        std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
-        return false;
-    }
-    return true;
-}
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-
-    std::vector<std::string> paths = {
-            // clang-format off
-            "/data/gsi/ota/test",
-            "/data/gsi/ota/test/super",
-            "/metadata/gsi/ota/test",
-            "/metadata/gsi/ota/test/super",
-            "/metadata/ota/test",
-            "/metadata/ota/test/snapshots",
-            // clang-format on
-    };
-    for (const auto& path : paths) {
-        if (!Mkdir(path)) {
-            return 1;
-        }
-    }
-
-    // Create this once, otherwise, gsid will start/stop between each test.
-    test_device = new TestDeviceInfo();
-    sm = SnapshotManager::New(test_device);
-    if (!sm) {
-        std::cerr << "Could not create snapshot manager\n";
-        return 1;
-    }
-
-    // Clean up previous run.
-    MetadataMountedTest().TearDown();
-    SnapshotUpdateTest().Cleanup();
-    SnapshotTest().Cleanup();
-
-    // Use a separate image manager for our fake super partition.
-    auto super_images = IImageManager::Open("ota/test/super", 10s);
-    if (!super_images) {
-        std::cerr << "Could not create image manager\n";
-        return 1;
-    }
-
-    // Clean up any old copy.
-    DeleteBackingImage(super_images.get(), "fake-super");
-
-    // Create and map the fake super partition.
-    static constexpr int kImageFlags =
-            IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
-    if (!super_images->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
-        std::cerr << "Could not create fake super partition\n";
-        return 1;
-    }
-    if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) {
-        std::cerr << "Could not map fake super partition\n";
-        return 1;
-    }
-    test_device->set_fake_super(fake_super);
-
-    auto result = RUN_ALL_TESTS();
-
-    DeleteBackingImage(super_images.get(), "fake-super");
-
-    return result;
-}
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
deleted file mode 100644
index 1bc0357..0000000
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-//
-// 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 <sysexits.h>
-
-#include <chrono>
-#include <iostream>
-#include <map>
-
-#include <android-base/logging.h>
-#include <libsnapshot/snapshot.h>
-
-using namespace std::string_literals;
-
-int Usage() {
-    std::cerr << "snapshotctl: Control snapshots.\n"
-                 "Usage: snapshotctl [action] [flags]\n"
-                 "Actions:\n"
-                 "  dump\n"
-                 "    Print snapshot states.\n"
-                 "  merge [--logcat]\n"
-                 "    Initialize merge and wait for it to be completed.\n"
-                 "    If --logcat is specified, log to logcat. Otherwise, log to stdout.\n";
-    return EX_USAGE;
-}
-
-namespace android {
-namespace snapshot {
-
-bool DumpCmdHandler(int /*argc*/, char** argv) {
-    android::base::InitLogging(argv, &android::base::StderrLogger);
-    return SnapshotManager::New()->Dump(std::cout);
-}
-
-bool MergeCmdHandler(int argc, char** argv) {
-    auto begin = std::chrono::steady_clock::now();
-
-    bool log_to_logcat = false;
-    for (int i = 2; i < argc; ++i) {
-        if (argv[i] == "--logcat"s) {
-            log_to_logcat = true;
-        }
-    }
-    if (log_to_logcat) {
-        android::base::InitLogging(argv);
-    } else {
-        android::base::InitLogging(argv, &android::base::StdioLogger);
-    }
-
-    auto state = SnapshotManager::New()->InitiateMergeAndWait();
-
-    if (state == UpdateState::None) {
-        return true;
-    }
-    if (state == UpdateState::MergeCompleted) {
-        auto end = std::chrono::steady_clock::now();
-        auto passed = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
-        LOG(INFO) << "Snapshot merged in " << passed << " ms.";
-        return true;
-    }
-
-    LOG(ERROR) << "Snapshot failed to merge with state \"" << state << "\".";
-    return false;
-}
-
-static std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {
-        // clang-format off
-        {"dump", DumpCmdHandler},
-        {"merge", MergeCmdHandler},
-        // clang-format on
-};
-
-}  // namespace snapshot
-}  // namespace android
-
-int main(int argc, char** argv) {
-    using namespace android::snapshot;
-    if (argc < 2) {
-        return Usage();
-    }
-
-    for (const auto& cmd : kCmdMap) {
-        if (cmd.first == argv[1]) {
-            return cmd.second(argc, argv) ? EX_OK : EX_SOFTWARE;
-        }
-    }
-
-    return Usage();
-}
diff --git a/fs_mgr/libsnapshot/snapshotctl.rc b/fs_mgr/libsnapshot/snapshotctl.rc
deleted file mode 100644
index 29707f1..0000000
--- a/fs_mgr/libsnapshot/snapshotctl.rc
+++ /dev/null
@@ -1,2 +0,0 @@
-on property:sys.boot_completed=1
-    exec - root root -- /system/bin/snapshotctl merge --logcat
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
deleted file mode 100644
index 312fa3e..0000000
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-// 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 "test_helpers.h"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <openssl/sha.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::ReadFully;
-using android::base::unique_fd;
-using android::base::WriteFully;
-using android::fiemap::IImageManager;
-using testing::AssertionFailure;
-using testing::AssertionSuccess;
-
-void DeleteBackingImage(IImageManager* manager, const std::string& name) {
-    if (manager->IsImageMapped(name)) {
-        ASSERT_TRUE(manager->UnmapImageDevice(name));
-    }
-    if (manager->BackingImageExists(name)) {
-        ASSERT_TRUE(manager->DeleteBackingImage(name));
-    }
-}
-
-android::base::unique_fd TestPartitionOpener::Open(const std::string& partition_name,
-                                                   int flags) const {
-    if (partition_name == "super") {
-        return PartitionOpener::Open(fake_super_path_, flags);
-    }
-    return PartitionOpener::Open(partition_name, flags);
-}
-
-bool TestPartitionOpener::GetInfo(const std::string& partition_name,
-                                  android::fs_mgr::BlockDeviceInfo* info) const {
-    if (partition_name == "super") {
-        return PartitionOpener::GetInfo(fake_super_path_, info);
-    }
-    return PartitionOpener::GetInfo(partition_name, info);
-}
-
-std::string TestPartitionOpener::GetDeviceString(const std::string& partition_name) const {
-    if (partition_name == "super") {
-        return fake_super_path_;
-    }
-    return PartitionOpener::GetDeviceString(partition_name);
-}
-
-bool WriteRandomData(const std::string& path) {
-    unique_fd rand(open("/dev/urandom", O_RDONLY));
-    unique_fd fd(open(path.c_str(), O_WRONLY));
-
-    char buf[4096];
-    while (true) {
-        ssize_t n = TEMP_FAILURE_RETRY(read(rand.get(), buf, sizeof(buf)));
-        if (n <= 0) return false;
-        if (!WriteFully(fd.get(), buf, n)) {
-            if (errno == ENOSPC) {
-                return true;
-            }
-            PLOG(ERROR) << "Cannot write " << path;
-            return false;
-        }
-    }
-}
-
-std::string ToHexString(const uint8_t* buf, size_t len) {
-    char lookup[] = "0123456789abcdef";
-    std::string out(len * 2 + 1, '\0');
-    char* outp = out.data();
-    for (; len > 0; len--, buf++) {
-        *outp++ = (char)lookup[*buf >> 4];
-        *outp++ = (char)lookup[*buf & 0xf];
-    }
-    return out;
-}
-
-std::optional<std::string> GetHash(const std::string& path) {
-    std::string content;
-    if (!android::base::ReadFileToString(path, &content, true)) {
-        PLOG(ERROR) << "Cannot access " << path;
-        return std::nullopt;
-    }
-    SHA256_CTX ctx;
-    SHA256_Init(&ctx);
-    SHA256_Update(&ctx, content.c_str(), content.size());
-    uint8_t out[32];
-    SHA256_Final(out, &ctx);
-    return ToHexString(out, sizeof(out));
-}
-
-AssertionResult FillFakeMetadata(MetadataBuilder* builder, const DeltaArchiveManifest& manifest,
-                                 const std::string& suffix) {
-    for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
-        if (!builder->AddGroup(group.name() + suffix, group.size())) {
-            return AssertionFailure()
-                   << "Cannot add group " << group.name() << " with size " << group.size();
-        }
-        for (const auto& partition_name : group.partition_names()) {
-            auto p = builder->AddPartition(partition_name + suffix, group.name() + suffix,
-                                           0 /* attr */);
-            if (!p) {
-                return AssertionFailure() << "Cannot add partition " << partition_name + suffix
-                                          << " to group " << group.name() << suffix;
-            }
-        }
-    }
-    for (const auto& partition : manifest.partitions()) {
-        auto p = builder->FindPartition(partition.partition_name() + suffix);
-        if (!p) {
-            return AssertionFailure() << "Cannot resize partition " << partition.partition_name()
-                                      << suffix << "; it is not found.";
-        }
-        if (!builder->ResizePartition(p, partition.new_partition_info().size())) {
-            return AssertionFailure()
-                   << "Cannot resize partition " << partition.partition_name() << suffix
-                   << " to size " << partition.new_partition_info().size();
-        }
-    }
-    return AssertionSuccess();
-}
-
-void SetSize(PartitionUpdate* partition_update, uint64_t size) {
-    partition_update->mutable_new_partition_info()->set_size(size);
-}
-
-uint64_t GetSize(PartitionUpdate* partition_update) {
-    return partition_update->mutable_new_partition_info()->size();
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
deleted file mode 100644
index 0f70afe..0000000
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ /dev/null
@@ -1,148 +0,0 @@
-// 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 <optional>
-#include <string>
-
-#include <android/hardware/boot/1.1/IBootControl.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/mock_property_fetcher.h>
-#include <liblp/partition_opener.h>
-#include <libsnapshot/snapshot.h>
-#include <storage_literals/storage_literals.h>
-#include <update_engine/update_metadata.pb.h>
-
-namespace android {
-namespace snapshot {
-
-using android::fs_mgr::IPropertyFetcher;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::testing::MockPropertyFetcher;
-using android::hardware::boot::V1_1::MergeStatus;
-using chromeos_update_engine::DeltaArchiveManifest;
-using chromeos_update_engine::PartitionUpdate;
-using testing::_;
-using testing::AssertionResult;
-using testing::NiceMock;
-using testing::Return;
-
-using namespace android::storage_literals;
-using namespace std::string_literals;
-
-// These are not reset between each test because it's expensive to create
-// these resources (starting+connecting to gsid, zero-filling images).
-extern std::unique_ptr<SnapshotManager> sm;
-extern class TestDeviceInfo* test_device;
-extern std::string fake_super;
-static constexpr uint64_t kSuperSize = 16_MiB + 4_KiB;
-static constexpr uint64_t kGroupSize = 16_MiB;
-
-// Redirect requests for "super" to our fake super partition.
-class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
-  public:
-    explicit TestPartitionOpener(const std::string& fake_super_path)
-        : fake_super_path_(fake_super_path) {}
-
-    android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
-    bool GetInfo(const std::string& partition_name,
-                 android::fs_mgr::BlockDeviceInfo* info) const override;
-    std::string GetDeviceString(const std::string& partition_name) const override;
-
-  private:
-    std::string fake_super_path_;
-};
-
-class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
-  public:
-    TestDeviceInfo() {}
-    explicit TestDeviceInfo(const std::string& fake_super) { set_fake_super(fake_super); }
-    TestDeviceInfo(const std::string& fake_super, const std::string& slot_suffix)
-        : TestDeviceInfo(fake_super) {
-        set_slot_suffix(slot_suffix);
-    }
-    std::string GetGsidDir() const override { return "ota/test"s; }
-    std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
-    std::string GetSlotSuffix() const override { return slot_suffix_; }
-    std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
-    std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
-    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
-        return *opener_.get();
-    }
-    bool SetBootControlMergeStatus(MergeStatus status) override {
-        merge_status_ = status;
-        return true;
-    }
-    bool IsOverlayfsSetup() const override { return false; }
-    bool IsRecovery() const override { return recovery_; }
-
-    void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
-    void set_fake_super(const std::string& path) {
-        opener_ = std::make_unique<TestPartitionOpener>(path);
-    }
-    void set_recovery(bool value) { recovery_ = value; }
-    MergeStatus merge_status() const { return merge_status_; }
-
-  private:
-    std::string slot_suffix_ = "_a";
-    std::unique_ptr<TestPartitionOpener> opener_;
-    MergeStatus merge_status_;
-    bool recovery_ = false;
-};
-
-class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
-  public:
-    SnapshotTestPropertyFetcher(const std::string& slot_suffix) {
-        ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix));
-        ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _))
-                .WillByDefault(Return(true));
-        ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
-                .WillByDefault(Return(false));
-        ON_CALL(*this, GetBoolProperty("ro.virtual_ab.enabled", _)).WillByDefault(Return(true));
-    }
-
-    static void SetUp(const std::string& slot_suffix = "_a") { Reset(slot_suffix); }
-
-    static void TearDown() { Reset("_a"); }
-
-  private:
-    static void Reset(const std::string& slot_suffix) {
-        IPropertyFetcher::OverrideForTesting(
-                std::make_unique<NiceMock<SnapshotTestPropertyFetcher>>(slot_suffix));
-    }
-};
-
-// Helper for error-spam-free cleanup.
-void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
-
-// Write some random data to the given device. Will write until reaching end of the device.
-bool WriteRandomData(const std::string& device);
-
-std::optional<std::string> GetHash(const std::string& path);
-
-// Add partitions and groups described by |manifest|.
-AssertionResult FillFakeMetadata(MetadataBuilder* builder, const DeltaArchiveManifest& manifest,
-                                 const std::string& suffix);
-
-// In the update package metadata, set a partition with the given size.
-void SetSize(PartitionUpdate* partition_update, uint64_t size);
-
-// Get partition size from update package metadata.
-uint64_t GetSize(PartitionUpdate* partition_update);
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
deleted file mode 100644
index 1b2f528..0000000
--- a/fs_mgr/libsnapshot/utility.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-// 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 <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <fs_mgr/roots.h>
-
-using android::dm::kSectorSize;
-using android::fs_mgr::EnsurePathMounted;
-using android::fs_mgr::EnsurePathUnmounted;
-using android::fs_mgr::Fstab;
-using android::fs_mgr::GetEntryForPath;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::Partition;
-using android::fs_mgr::ReadDefaultFstab;
-
-namespace android {
-namespace snapshot {
-
-void AutoDevice::Release() {
-    name_.clear();
-}
-
-AutoDeviceList::~AutoDeviceList() {
-    // Destroy devices in the reverse order because newer devices may have dependencies
-    // on older devices.
-    for (auto it = devices_.rbegin(); it != devices_.rend(); ++it) {
-        it->reset();
-    }
-}
-
-void AutoDeviceList::Release() {
-    for (auto&& p : devices_) {
-        p->Release();
-    }
-}
-
-AutoUnmapDevice::~AutoUnmapDevice() {
-    if (name_.empty()) return;
-    if (!dm_->DeleteDeviceIfExists(name_)) {
-        LOG(ERROR) << "Failed to auto unmap device " << name_;
-    }
-}
-
-AutoUnmapImage::~AutoUnmapImage() {
-    if (name_.empty()) return;
-    if (!images_->UnmapImageIfExists(name_)) {
-        LOG(ERROR) << "Failed to auto unmap cow image " << name_;
-    }
-}
-
-std::vector<Partition*> ListPartitionsWithSuffix(MetadataBuilder* builder,
-                                                 const std::string& suffix) {
-    std::vector<Partition*> ret;
-    for (const auto& group : builder->ListGroups()) {
-        for (auto* partition : builder->ListPartitionsInGroup(group)) {
-            if (!base::EndsWith(partition->name(), suffix)) {
-                continue;
-            }
-            ret.push_back(partition);
-        }
-    }
-    return ret;
-}
-
-AutoDeleteSnapshot::~AutoDeleteSnapshot() {
-    if (!name_.empty() && !manager_->DeleteSnapshot(lock_, name_)) {
-        LOG(ERROR) << "Failed to auto delete snapshot " << name_;
-    }
-}
-
-bool InitializeCow(const std::string& device) {
-    // When the kernel creates a persistent dm-snapshot, it requires a CoW file
-    // to store the modifications. The kernel interface does not specify how
-    // the CoW is used, and there is no standard associated.
-    // By looking at the current implementation, the CoW file is treated as:
-    // - a _NEW_ snapshot if its first 32 bits are zero, so the newly created
-    // dm-snapshot device will look like a perfect copy of the origin device;
-    // - an _EXISTING_ snapshot if the first 32 bits are equal to a
-    // kernel-specified magic number and the CoW file metadata is set as valid,
-    // so it can be used to resume the last state of a snapshot device;
-    // - an _INVALID_ snapshot otherwise.
-    // To avoid zero-filling the whole CoW file when a new dm-snapshot is
-    // created, here we zero-fill only the first chunk to be compliant with
-    // lvm.
-    constexpr ssize_t kDmSnapZeroFillSize = kSectorSize * kSnapshotChunkSize;
-
-    std::vector<uint8_t> zeros(kDmSnapZeroFillSize, 0);
-    android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
-    if (fd < 0) {
-        PLOG(ERROR) << "Can't open COW device: " << device;
-        return false;
-    }
-
-    LOG(INFO) << "Zero-filling COW device: " << device;
-    if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
-        PLOG(ERROR) << "Can't zero-fill COW device for " << device;
-        return false;
-    }
-    return true;
-}
-
-std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
-        LOG(ERROR) << "Cannot read default fstab";
-        return nullptr;
-    }
-
-    if (GetEntryForPath(&fstab, path) == nullptr) {
-        LOG(INFO) << "EnsureMetadataMounted can't find entry for " << path << ", skipping";
-        return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice("", {}));
-    }
-
-    if (!EnsurePathMounted(&fstab, path)) {
-        LOG(ERROR) << "Cannot mount " << path;
-        return nullptr;
-    }
-    return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice(path, std::move(fstab)));
-}
-
-AutoUnmountDevice::~AutoUnmountDevice() {
-    if (name_.empty()) return;
-    if (!EnsurePathUnmounted(&fstab_, name_)) {
-        LOG(ERROR) << "Cannot unmount " << name_;
-    }
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
deleted file mode 100644
index 0c08ed2..0000000
--- a/fs_mgr/libsnapshot/utility.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// 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 <functional>
-#include <string>
-
-#include <android-base/macros.h>
-#include <fstab/fstab.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/builder.h>
-#include <libsnapshot/snapshot.h>
-#include <update_engine/update_metadata.pb.h>
-
-#include <libsnapshot/auto_device.h>
-
-namespace android {
-namespace snapshot {
-
-// Unit is sectors, this is a 4K chunk.
-static constexpr uint32_t kSnapshotChunkSize = 8;
-
-// A list of devices we created along the way.
-// - Whenever a device is created that is subject to GC'ed at the end of
-//   this function, add it to this list.
-// - If any error has occurred, the list is destroyed, and all these devices
-//   are cleaned up.
-// - Upon success, Release() should be called so that the created devices
-//   are kept.
-struct AutoDeviceList {
-    ~AutoDeviceList();
-    template <typename T, typename... Args>
-    void EmplaceBack(Args&&... args) {
-        devices_.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));
-    }
-    void Release();
-
-  private:
-    std::vector<std::unique_ptr<AutoDevice>> devices_;
-};
-
-// Automatically unmap a device upon deletion.
-struct AutoUnmapDevice : AutoDevice {
-    // On destruct, delete |name| from device mapper.
-    AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
-        : AutoDevice(name), dm_(dm) {}
-    AutoUnmapDevice(AutoUnmapDevice&& other) = default;
-    ~AutoUnmapDevice();
-
-  private:
-    DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
-    android::dm::DeviceMapper* dm_ = nullptr;
-};
-
-// Automatically unmap an image upon deletion.
-struct AutoUnmapImage : AutoDevice {
-    // On destruct, delete |name| from image manager.
-    AutoUnmapImage(android::fiemap::IImageManager* images, const std::string& name)
-        : AutoDevice(name), images_(images) {}
-    AutoUnmapImage(AutoUnmapImage&& other) = default;
-    ~AutoUnmapImage();
-
-  private:
-    DISALLOW_COPY_AND_ASSIGN(AutoUnmapImage);
-    android::fiemap::IImageManager* images_ = nullptr;
-};
-
-// Automatically deletes a snapshot. |name| should be the name of the partition, e.g. "system_a".
-// Client is responsible for maintaining the lifetime of |manager| and |lock|.
-struct AutoDeleteSnapshot : AutoDevice {
-    AutoDeleteSnapshot(SnapshotManager* manager, SnapshotManager::LockedFile* lock,
-                       const std::string& name)
-        : AutoDevice(name), manager_(manager), lock_(lock) {}
-    AutoDeleteSnapshot(AutoDeleteSnapshot&& other);
-    ~AutoDeleteSnapshot();
-
-  private:
-    DISALLOW_COPY_AND_ASSIGN(AutoDeleteSnapshot);
-    SnapshotManager* manager_ = nullptr;
-    SnapshotManager::LockedFile* lock_ = nullptr;
-};
-
-struct AutoUnmountDevice : AutoDevice {
-    // Empty object that does nothing.
-    AutoUnmountDevice() : AutoDevice("") {}
-    static std::unique_ptr<AutoUnmountDevice> New(const std::string& path);
-    ~AutoUnmountDevice();
-
-  private:
-    AutoUnmountDevice(const std::string& path, android::fs_mgr::Fstab&& fstab)
-        : AutoDevice(path), fstab_(std::move(fstab)) {}
-    android::fs_mgr::Fstab fstab_;
-};
-
-// Return a list of partitions in |builder| with the name ending in |suffix|.
-std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(
-        android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);
-
-// Initialize a device before using it as the COW device for a dm-snapshot device.
-bool InitializeCow(const std::string& device);
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
deleted file mode 100644
index 11611dd..0000000
--- a/fs_mgr/libstorage_literals/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-
-cc_library_headers {
-    name: "libstorage_literals_headers",
-    host_supported: true,
-    export_include_dirs: ["."],
-}
diff --git a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
deleted file mode 100644
index ac0dfbd..0000000
--- a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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 <stdlib.h>
-
-namespace android {
-namespace storage_literals {
-
-template <size_t Power>
-struct Size {
-    static constexpr size_t power = Power;
-    explicit constexpr Size(uint64_t count) : value_(count) {}
-
-    constexpr uint64_t bytes() const { return value_ << power; }
-    constexpr uint64_t count() const { return value_; }
-    constexpr operator uint64_t() const { return bytes(); }
-
-  private:
-    uint64_t value_;
-};
-
-using B = Size<0>;
-using KiB = Size<10>;
-using MiB = Size<20>;
-using GiB = Size<30>;
-
-constexpr B operator""_B(unsigned long long v) {  // NOLINT
-    return B{v};
-}
-
-constexpr KiB operator""_KiB(unsigned long long v) {  // NOLINT
-    return KiB{v};
-}
-
-constexpr MiB operator""_MiB(unsigned long long v) {  // NOLINT
-    return MiB{v};
-}
-
-constexpr GiB operator""_GiB(unsigned long long v) {  // NOLINT
-    return GiB{v};
-}
-
-template <typename Dest, typename Src>
-constexpr Dest size_cast(Src src) {
-    if (Src::power < Dest::power) {
-        return Dest(src.count() >> (Dest::power - Src::power));
-    }
-    if (Src::power > Dest::power) {
-        return Dest(src.count() << (Src::power - Dest::power));
-    }
-    return Dest(src.count());
-}
-
-static_assert(1_B == 1);
-static_assert(1_KiB == 1 << 10);
-static_assert(1_MiB == 1 << 20);
-static_assert(1_GiB == 1 << 30);
-static_assert(size_cast<KiB>(1_B).count() == 0);
-static_assert(size_cast<KiB>(1024_B).count() == 1);
-static_assert(size_cast<KiB>(1_MiB).count() == 1024);
-
-}  // namespace storage_literals
-}  // namespace android
diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp
deleted file mode 100644
index 937e0f3..0000000
--- a/fs_mgr/libvbmeta/Android.bp
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// 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.
-//
-
-libvbmeta_lib_deps = [
-    "libbase",
-    "libcrypto",
-]
-
-cc_library {
-    name: "libvbmeta",
-    host_supported: true,
-    srcs: [
-        "builder.cpp",
-        "reader.cpp",
-        "utility.cpp",
-        "writer.cpp",
-    ],
-    shared_libs: [
-        "liblog",
-    ] + libvbmeta_lib_deps,
-    export_include_dirs: ["include"],
-}
-
-cc_test_host {
-    name: "libvbmeta_test",
-    static_libs: [
-        "libsparse",
-        "libvbmeta",
-        "libz",
-    ] + libvbmeta_lib_deps,
-    srcs: [
-        "builder_test.cpp",
-        "super_vbmeta_test.cpp",
-    ],
-    required: [
-        "avbtool",
-        "vbmake",
-    ],
-}
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/builder.cpp b/fs_mgr/libvbmeta/builder.cpp
deleted file mode 100644
index a901a4f..0000000
--- a/fs_mgr/libvbmeta/builder.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * 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 "builder.h"
-
-#include <android-base/file.h>
-#include <openssl/sha.h>
-
-#include "reader.h"
-#include "utility.h"
-#include "writer.h"
-
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-using android::base::unique_fd;
-
-namespace android {
-namespace fs_mgr {
-
-SuperVBMetaBuilder::SuperVBMetaBuilder() {}
-
-SuperVBMetaBuilder::SuperVBMetaBuilder(const int super_vbmeta_fd,
-                                       const std::map<std::string, std::string>& images_path)
-    : super_vbmeta_fd_(super_vbmeta_fd), images_path_(images_path) {}
-
-Result<void> SuperVBMetaBuilder::Build() {
-    for (const auto& [vbmeta_name, file_path] : images_path_) {
-        Result<std::string> content = ReadVBMetaImageFromFile(file_path);
-        if (!content) {
-            return content.error();
-        }
-
-        Result<uint8_t> vbmeta_index = AddVBMetaImage(vbmeta_name);
-        if (!vbmeta_index) {
-            return vbmeta_index.error();
-        }
-
-        Result<void> rv_export_vbmeta_image =
-                ExportVBMetaImageToFile(vbmeta_index.value(), content.value());
-        if (!rv_export_vbmeta_image) {
-            return rv_export_vbmeta_image;
-        }
-    }
-    return {};
-}
-
-Result<std::string> SuperVBMetaBuilder::ReadVBMetaImageFromFile(const std::string& file) {
-    unique_fd source_fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (source_fd < 0) {
-        return ErrnoError() << "Couldn't open vbmeta image file " << file;
-    }
-
-    Result<uint64_t> file_size = GetFileSize(source_fd);
-    if (!file_size) {
-        return file_size.error();
-    }
-
-    if (file_size.value() > VBMETA_IMAGE_MAX_SIZE) {
-        return Error() << "vbmeta image file size " << file_size.value() << " is too large";
-    }
-
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
-    if (!android::base::ReadFully(source_fd, buffer.get(), file_size.value())) {
-        return ErrnoError() << "Couldn't read vbmeta image file " << file;
-    }
-
-    return std::string(reinterpret_cast<const char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
-}
-
-Result<uint8_t> SuperVBMetaBuilder::GetEmptySlot() {
-    for (uint8_t i = 0; i < VBMETA_IMAGE_MAX_NUM; ++i) {
-        if ((table_.header.in_use & (1 << i)) == 0) return i;
-    }
-    return Error() << "There isn't empty slot in super vbmeta";
-}
-
-Result<uint8_t> SuperVBMetaBuilder::AddVBMetaImage(const std::string& vbmeta_name) {
-    auto desc = std::find_if(
-            table_.descriptors.begin(), table_.descriptors.end(),
-            [&vbmeta_name](const auto& entry) { return entry.vbmeta_name == vbmeta_name; });
-
-    uint8_t slot_number = 0;
-    if (desc != table_.descriptors.end()) {
-        slot_number = desc->vbmeta_index;
-    } else {
-        Result<uint8_t> new_slot = GetEmptySlot();
-        if (!new_slot) {
-            return new_slot;
-        }
-        slot_number = new_slot.value();
-
-        // insert new descriptor into table
-        InternalVBMetaDescriptor new_desc;
-        new_desc.vbmeta_index = slot_number;
-        new_desc.vbmeta_name_length = vbmeta_name.length();
-        new_desc.vbmeta_name = vbmeta_name;
-        memset(new_desc.reserved, 0, sizeof(new_desc.reserved));
-        table_.descriptors.emplace_back(std::move(new_desc));
-
-        // mark slot as in use
-        table_.header.in_use |= (1 << slot_number);
-    }
-
-    return slot_number;
-}
-
-void SuperVBMetaBuilder::DeleteVBMetaImage(const std::string& vbmeta_name) {
-    auto desc = std::find_if(
-            table_.descriptors.begin(), table_.descriptors.end(),
-            [&vbmeta_name](const auto& entry) { return entry.vbmeta_name == vbmeta_name; });
-
-    if (desc != table_.descriptors.end()) {
-        // mark slot as not in use
-        table_.header.in_use &= ~(1 << desc->vbmeta_index);
-
-        // erase descriptor in table
-        table_.descriptors.erase(desc);
-    }
-}
-
-std::unique_ptr<VBMetaTable> SuperVBMetaBuilder::ExportVBMetaTable() {
-    // calculate descriptors size
-    uint32_t descriptors_size = 0;
-    for (const auto& desc : table_.descriptors) {
-        descriptors_size += SUPER_VBMETA_DESCRIPTOR_SIZE + desc.vbmeta_name_length * sizeof(char);
-    }
-
-    // export header
-    table_.header.magic = SUPER_VBMETA_MAGIC;
-    table_.header.major_version = SUPER_VBMETA_MAJOR_VERSION;
-    table_.header.minor_version = SUPER_VBMETA_MINOR_VERSION;
-    table_.header.header_size = SUPER_VBMETA_HEADER_SIZE;
-    table_.header.total_size = SUPER_VBMETA_HEADER_SIZE + descriptors_size;
-    memset(table_.header.checksum, 0, sizeof(table_.header.checksum));
-    table_.header.descriptors_size = descriptors_size;
-    memset(table_.header.reserved, 0, sizeof(table_.header.reserved));
-    std::string serialized_table = SerializeVBMetaTable(table_);
-    ::SHA256(reinterpret_cast<const uint8_t*>(serialized_table.c_str()), table_.header.total_size,
-             &table_.header.checksum[0]);
-
-    return std::make_unique<VBMetaTable>(table_);
-}
-
-Result<void> SuperVBMetaBuilder::ExportVBMetaTableToFile() {
-    std::unique_ptr<VBMetaTable> table = ExportVBMetaTable();
-
-    std::string serialized_table = SerializeVBMetaTable(*table);
-
-    android::base::Result<void> rv_write_primary_vbmeta_table =
-            WritePrimaryVBMetaTable(super_vbmeta_fd_, serialized_table);
-    if (!rv_write_primary_vbmeta_table) {
-        return rv_write_primary_vbmeta_table;
-    }
-
-    android::base::Result<void> rv_write_backup_vbmeta_table =
-            WriteBackupVBMetaTable(super_vbmeta_fd_, serialized_table);
-    return rv_write_backup_vbmeta_table;
-}
-
-Result<void> SuperVBMetaBuilder::ExportVBMetaImageToFile(const uint8_t vbmeta_index,
-                                                         const std::string& vbmeta_image) {
-    Result<void> rv_write_vbmeta_image =
-            WriteVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);
-    if (!rv_write_vbmeta_image) {
-        return rv_write_vbmeta_image;
-    }
-
-    Result<void> rv_validate_vbmeta_image =
-            ValidateVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);
-    return rv_validate_vbmeta_image;
-}
-
-bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
-                            const std::map<std::string, std::string>& images_path) {
-    unique_fd super_vbmeta_fd(TEMP_FAILURE_RETRY(
-            open(super_vbmeta_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644)));
-    if (super_vbmeta_fd < 0) {
-        PERROR << "Couldn't open super vbmeta file " << super_vbmeta_file;
-        return false;
-    }
-
-    SuperVBMetaBuilder builder(super_vbmeta_fd, images_path);
-
-    Result<void> rv_build = builder.Build();
-    if (!rv_build) {
-        LERROR << rv_build.error();
-        return false;
-    }
-
-    Result<void> rv_export = builder.ExportVBMetaTableToFile();
-    if (!rv_export) {
-        LERROR << rv_export.error();
-        return false;
-    }
-
-    return true;
-}
-
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/builder.h b/fs_mgr/libvbmeta/builder.h
deleted file mode 100644
index 58ea36a..0000000
--- a/fs_mgr/libvbmeta/builder.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 <map>
-#include <string>
-
-#include <android-base/result.h>
-
-#include "super_vbmeta_format.h"
-
-namespace android {
-namespace fs_mgr {
-
-class SuperVBMetaBuilder {
-  public:
-    SuperVBMetaBuilder();
-    SuperVBMetaBuilder(const int super_vbmeta_fd,
-                       const std::map<std::string, std::string>& images_path);
-    android::base::Result<void> Build();
-    android::base::Result<std::string> ReadVBMetaImageFromFile(const std::string& file);
-    // It adds the vbmeta image in super_vbmeta and returns the index
-    // (which has the same meaning with vbmeta_index of VBMetaDescriptor).
-    android::base::Result<uint8_t> AddVBMetaImage(const std::string& vbmeta_name);
-    void DeleteVBMetaImage(const std::string& vbmeta_name);
-    std::unique_ptr<VBMetaTable> ExportVBMetaTable();
-    android::base::Result<void> ExportVBMetaTableToFile();
-    android::base::Result<void> ExportVBMetaImageToFile(const uint8_t vbmeta_index,
-                                                        const std::string& vbmeta_image);
-
-  private:
-    android::base::Result<uint8_t> GetEmptySlot();
-
-    int super_vbmeta_fd_;
-    VBMetaTable table_;
-    // Maps vbmeta image name to vbmeta image file path.
-    std::map<std::string, std::string> images_path_;
-};
-
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/builder_test.cpp b/fs_mgr/libvbmeta/builder_test.cpp
deleted file mode 100644
index 9a015fd..0000000
--- a/fs_mgr/libvbmeta/builder_test.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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 "builder.h"
-#include "super_vbmeta_format.h"
-
-using android::base::Result;
-using android::fs_mgr::SuperVBMetaBuilder;
-
-TEST(BuilderTest, VBMetaTableBasic) {
-    std::unique_ptr<SuperVBMetaBuilder> builder = std::make_unique<SuperVBMetaBuilder>();
-    ASSERT_NE(builder, nullptr);
-
-    Result<uint8_t> vbmeta_index = builder->AddVBMetaImage("vbmeta" /* vbmeta_name */
-    );
-    EXPECT_TRUE(vbmeta_index);
-
-    Result<uint8_t> vbmeta_system_slot = builder->AddVBMetaImage("vbmeta_system" /* vbmeta_name */
-    );
-    EXPECT_TRUE(vbmeta_system_slot);
-
-    Result<uint8_t> vbmeta_vendor_slot = builder->AddVBMetaImage("vbmeta_vendor" /* vbmeta_name */
-    );
-    EXPECT_TRUE(vbmeta_vendor_slot);
-
-    builder->DeleteVBMetaImage("vbmeta_system" /* vbmeta_name */
-    );
-
-    Result<uint8_t> vbmeta_product_slot = builder->AddVBMetaImage("vbmeta_product" /* vbmeta_name */
-    );
-    EXPECT_TRUE(vbmeta_product_slot);
-
-    std::unique_ptr<VBMetaTable> table = builder->ExportVBMetaTable();
-    ASSERT_NE(table, nullptr);
-
-    // check for vbmeta table header
-    EXPECT_EQ(table->header.magic, SUPER_VBMETA_MAGIC);
-    EXPECT_EQ(table->header.major_version, SUPER_VBMETA_MAJOR_VERSION);
-    EXPECT_EQ(table->header.minor_version, SUPER_VBMETA_MINOR_VERSION);
-    EXPECT_EQ(table->header.header_size, SUPER_VBMETA_HEADER_SIZE);
-    EXPECT_EQ(table->header.total_size,
-              SUPER_VBMETA_HEADER_SIZE + SUPER_VBMETA_DESCRIPTOR_SIZE * 3 + 33);
-    EXPECT_EQ(table->header.descriptors_size, SUPER_VBMETA_DESCRIPTOR_SIZE * 3 + 33);
-
-    // Test for vbmeta table descriptors
-    EXPECT_EQ(table->descriptors.size(), 3);
-
-    EXPECT_EQ(table->descriptors[0].vbmeta_index, 0);
-    EXPECT_EQ(table->descriptors[0].vbmeta_name_length, 6);
-    for (int i = 0; i < sizeof(table->descriptors[0].reserved); i++)
-        EXPECT_EQ(table->descriptors[0].reserved[i], 0);
-    EXPECT_EQ(table->descriptors[0].vbmeta_name, "vbmeta");
-
-    EXPECT_EQ(table->descriptors[1].vbmeta_index, 2);
-    EXPECT_EQ(table->descriptors[1].vbmeta_name_length, 13);
-    for (int i = 0; i < sizeof(table->descriptors[1].reserved); i++)
-        EXPECT_EQ(table->descriptors[1].reserved[i], 0);
-    EXPECT_EQ(table->descriptors[1].vbmeta_name, "vbmeta_vendor");
-
-    EXPECT_EQ(table->descriptors[2].vbmeta_index, 1);
-    EXPECT_EQ(table->descriptors[2].vbmeta_name_length, 14);
-    for (int i = 0; i < sizeof(table->descriptors[2].reserved); i++)
-        EXPECT_EQ(table->descriptors[2].reserved[i], 0);
-    EXPECT_EQ(table->descriptors[2].vbmeta_name, "vbmeta_product");
-}
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/reader.cpp b/fs_mgr/libvbmeta/reader.cpp
deleted file mode 100644
index 212d186..0000000
--- a/fs_mgr/libvbmeta/reader.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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 "reader.h"
-
-#include <android-base/file.h>
-
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-
-namespace android {
-namespace fs_mgr {
-
-Result<void> LoadAndVerifySuperVBMetaHeader(const void* buffer, SuperVBMetaHeader* header) {
-    memcpy(header, buffer, sizeof(*header));
-
-    // Do basic validation of super vbmeta.
-    if (header->magic != SUPER_VBMETA_MAGIC) {
-        return Error() << "Super VBMeta has invalid magic value";
-    }
-
-    // Check that the version is compatible.
-    if (header->major_version != SUPER_VBMETA_MAJOR_VERSION ||
-        header->minor_version > SUPER_VBMETA_MINOR_VERSION) {
-        return Error() << "Super VBMeta has incompatible version";
-    }
-    return {};
-}
-
-void LoadVBMetaDescriptors(const void* buffer, uint32_t size,
-                           std::vector<InternalVBMetaDescriptor>* descriptors) {
-    for (int p = 0; p < size;) {
-        InternalVBMetaDescriptor descriptor;
-        memcpy(&descriptor, (char*)buffer + p, SUPER_VBMETA_DESCRIPTOR_SIZE);
-        p += SUPER_VBMETA_DESCRIPTOR_SIZE;
-
-        descriptor.vbmeta_name = std::string((char*)buffer + p, descriptor.vbmeta_name_length);
-        p += descriptor.vbmeta_name_length;
-
-        descriptors->emplace_back(std::move(descriptor));
-    }
-}
-
-Result<void> ReadVBMetaTable(int fd, uint64_t offset, VBMetaTable* table) {
-    std::unique_ptr<uint8_t[]> header_buffer =
-            std::make_unique<uint8_t[]>(SUPER_VBMETA_HEADER_SIZE);
-    if (!android::base::ReadFullyAtOffset(fd, header_buffer.get(), SUPER_VBMETA_HEADER_SIZE,
-                                          offset)) {
-        return ErrnoError() << "Couldn't read super vbmeta header at offset " << offset;
-    }
-
-    Result<void> rv_header = LoadAndVerifySuperVBMetaHeader(header_buffer.get(), &table->header);
-    if (!rv_header) {
-        return rv_header;
-    }
-
-    const uint64_t descriptors_offset = offset + table->header.header_size;
-    std::unique_ptr<uint8_t[]> descriptors_buffer =
-            std::make_unique<uint8_t[]>(table->header.descriptors_size);
-    if (!android::base::ReadFullyAtOffset(fd, descriptors_buffer.get(),
-                                          table->header.descriptors_size, descriptors_offset)) {
-        return ErrnoError() << "Couldn't read super vbmeta descriptors at offset "
-                            << descriptors_offset;
-    }
-
-    LoadVBMetaDescriptors(descriptors_buffer.get(), table->header.descriptors_size,
-                          &table->descriptors);
-    return {};
-}
-
-Result<void> ReadPrimaryVBMetaTable(int fd, VBMetaTable* table) {
-    uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET;
-    return ReadVBMetaTable(fd, offset, table);
-}
-
-Result<void> ReadBackupVBMetaTable(int fd, VBMetaTable* table) {
-    uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET;
-    return ReadVBMetaTable(fd, offset, table);
-}
-
-Result<std::string> ReadVBMetaImage(int fd, int slot) {
-    const uint64_t offset = 2 * SUPER_VBMETA_TABLE_MAX_SIZE + slot * VBMETA_IMAGE_MAX_SIZE;
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
-    if (!android::base::ReadFullyAtOffset(fd, buffer.get(), VBMETA_IMAGE_MAX_SIZE, offset)) {
-        return ErrnoError() << "Couldn't read vbmeta image at offset " << offset;
-    }
-    return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
-}
-
-Result<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,
-                                 const std::string& vbmeta_image) {
-    Result<std::string> content = ReadVBMetaImage(super_vbmeta_fd, vbmeta_index);
-    if (!content) {
-        return content.error();
-    }
-
-    if (vbmeta_image != content.value()) {
-        return Error() << "VBMeta Image in Super VBMeta differ from the original one.";
-    }
-    return {};
-}
-
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/fs_mgr/libvbmeta/reader.h b/fs_mgr/libvbmeta/reader.h
deleted file mode 100644
index f0997ff..0000000
--- a/fs_mgr/libvbmeta/reader.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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-base/result.h>
-
-#include "super_vbmeta_format.h"
-
-namespace android {
-namespace fs_mgr {
-
-android::base::Result<void> ReadPrimaryVBMetaTable(int fd, VBMetaTable* table);
-android::base::Result<void> ReadBackupVBMetaTable(int fd, VBMetaTable* table);
-android::base::Result<std::string> ReadVBMetaImage(int fd, int slot);
-
-android::base::Result<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,
-                                                const std::string& vbmeta_image);
-
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/super_vbmeta_format.h b/fs_mgr/libvbmeta/super_vbmeta_format.h
deleted file mode 100644
index c62a2dd..0000000
--- a/fs_mgr/libvbmeta/super_vbmeta_format.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.
- */
-
-/* This .h file is intended for CPP clients (usually fastbootd, recovery and update_engine)  */
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include "super_vbmeta_format_c.h"
-
-struct InternalVBMetaDescriptor : VBMetaDescriptor {
-    /*  64: The vbmeta image's name */
-    std::string vbmeta_name;
-};
-
-struct VBMetaTable {
-    SuperVBMetaHeader header;
-    std::vector<InternalVBMetaDescriptor> descriptors;
-};
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/super_vbmeta_format_c.h b/fs_mgr/libvbmeta/super_vbmeta_format_c.h
deleted file mode 100644
index 6d79801..0000000
--- a/fs_mgr/libvbmeta/super_vbmeta_format_c.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.
- */
-
-/* This .h file is intended for C clients (usually bootloader).  */
-
-#pragma once
-
-#include <stdint.h>
-
-/* Magic signature for super vbmeta. */
-#define SUPER_VBMETA_MAGIC 0x5356424d
-
-/* Current super vbmeta version. */
-#define SUPER_VBMETA_MAJOR_VERSION 1
-#define SUPER_VBMETA_MINOR_VERSION 0
-
-/* super vbmeta size. */
-#define SUPER_VBMETA_HEADER_SIZE sizeof(SuperVBMetaHeader)
-#define SUPER_VBMETA_DESCRIPTOR_SIZE sizeof(VBMetaDescriptor)
-#define SUPER_VBMETA_TABLE_MAX_SIZE 2048
-
-/* super vbmeta offset. */
-#define PRIMARY_SUPER_VBMETA_TABLE_OFFSET 0
-#define BACKUP_SUPER_VBMETA_TABLE_OFFSET SUPER_VBMETA_TABLE_MAX_SIZE
-
-/* restriction of vbmeta image */
-#define VBMETA_IMAGE_MAX_NUM 32
-#define VBMETA_IMAGE_MAX_SIZE 64 * 1024
-
-/* Binary format of the super vbmeta image.
- *
- * The super vbmeta image consists of two blocks:
- *
- *  +------------------------------------------+
- *  | Super VBMeta Table - fixed size          |
- *  +------------------------------------------+
- *  | Backup Super VBMeta Table - fixed size   |
- *  +------------------------------------------+
- *  | VBMeta Images - fixed size               |
- *  +------------------------------------------+
- *
- *  The "Super VBMeta Table" records the offset
- *  and the size of each vbmeta_partition within
- *  /super_vbmeta.
- *
- *  The "VBMeta Image" is copied from each vbmeta_partition
- *  and filled with 0 until 64K bytes.
- *
- * The super vbmeta table consists of two blocks:
- *
- *  +-----------------------------------------+
- *  | Header data - fixed size                |
- *  +-----------------------------------------+
- *  | VBMeta descriptors - variable size      |
- *  +-----------------------------------------+
- *
- * The "Header data" block is described by the following struct and
- * is always 128 bytes long.
- *
- * The "VBMeta descriptor" is |descriptors_size| + |vbmeta_name_length|
- * bytes long. It contains the offset and size for each vbmeta image
- * and is followed by |vbmeta_name_length| bytes of the partition name
- * (UTF-8 encoded).
- */
-
-typedef struct SuperVBMetaHeader {
-    /*  0: Magic signature (SUPER_VBMETA_MAGIC). */
-    uint32_t magic;
-
-    /*  4: Major version. Version number required to read this super vbmeta. If the version is not
-     * equal to the library version, the super vbmeta should be considered incompatible.
-     */
-    uint16_t major_version;
-
-    /*  6: Minor version. A library supporting newer features should be able to
-     * read super vbmeta with an older minor version. However, an older library
-     * should not support reading super vbmeta if its minor version is higher.
-     */
-    uint16_t minor_version;
-
-    /*  8: The size of this header struct. */
-    uint32_t header_size;
-
-    /*  12: The size of this super vbmeta table. */
-    uint32_t total_size;
-
-    /*  16: SHA256 checksum of this super vbmeta table, with this field set to 0. */
-    uint8_t checksum[32];
-
-    /*  48: The size of the vbmeta table descriptors. */
-    uint32_t descriptors_size;
-
-    /*  52: mark which slot is in use. */
-    uint32_t in_use = 0;
-
-    /*  56: reserved for other usage, filled with 0. */
-    uint8_t reserved[72];
-} __attribute__((packed)) SuperVBMetaHeader;
-
-typedef struct VBMetaDescriptor {
-    /*  0: The slot number of the vbmeta image. */
-    uint8_t vbmeta_index;
-
-    /*  12: The length of the vbmeta image name. */
-    uint32_t vbmeta_name_length;
-
-    /*  16: Space reserved for other usage, filled with 0. */
-    uint8_t reserved[48];
-} __attribute__((packed)) VBMetaDescriptor;
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/super_vbmeta_test.cpp b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
deleted file mode 100644
index 6b4fc5d..0000000
--- a/fs_mgr/libvbmeta/super_vbmeta_test.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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/types.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-#include <openssl/sha.h>
-#include <sparse/sparse.h>
-
-#include "reader.h"
-#include "super_vbmeta_format.h"
-#include "utility.h"
-#include "writer.h"
-
-#define FAKE_DATA_SIZE 40960
-#define FAKE_PARTITION_SIZE FAKE_DATA_SIZE * 25
-
-using android::base::Result;
-using android::fs_mgr::GetFileSize;
-using android::fs_mgr::ReadVBMetaImage;
-using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
-
-void GeneratePartitionImage(int fd, const std::string& file_name,
-                            const std::string& partition_name) {
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(FAKE_DATA_SIZE);
-    for (size_t c = 0; c < FAKE_DATA_SIZE; c++) {
-        buffer[c] = uint8_t(c);
-    }
-
-    SparsePtr file(sparse_file_new(512 /* block size */, FAKE_DATA_SIZE), sparse_file_destroy);
-    EXPECT_TRUE(file);
-    EXPECT_EQ(0, sparse_file_add_data(file.get(), buffer.get(), FAKE_DATA_SIZE,
-                                      0 /* offset in blocks */));
-    EXPECT_EQ(0, sparse_file_write(file.get(), fd, false /* gz */, true /* sparse */,
-                                   false /* crc */));
-
-    std::stringstream cmd;
-    cmd << "avbtool add_hashtree_footer"
-        << " --image " << file_name << " --partition_name " << partition_name
-        << " --partition_size " << FAKE_PARTITION_SIZE << " --algorithm SHA256_RSA2048"
-        << " --key external/avb/test/data/testkey_rsa2048.pem";
-
-    int rc = system(cmd.str().c_str());
-    EXPECT_TRUE(WIFEXITED(rc));
-    EXPECT_EQ(WEXITSTATUS(rc), 0);
-}
-
-void GenerateVBMetaImage(const std::string& vbmeta_file_name,
-                         const std::string& include_file_name) {
-    std::stringstream cmd;
-    cmd << "avbtool make_vbmeta_image"
-        << " --output " << vbmeta_file_name << " --include_descriptors_from_image "
-        << include_file_name;
-
-    int rc = system(cmd.str().c_str());
-    EXPECT_TRUE(WIFEXITED(rc));
-    EXPECT_EQ(WEXITSTATUS(rc), 0);
-}
-
-std::string ReadVBMetaImageFromFile(const std::string& file) {
-    android::base::unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
-    EXPECT_GT(fd, 0);
-    Result<uint64_t> file_size = GetFileSize(fd);
-    EXPECT_TRUE(file_size);
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
-    EXPECT_TRUE(android::base::ReadFully(fd, buffer.get(), file_size.value()));
-    return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
-}
-
-TEST(VBMetaTableTest, VBMetaTableBasic) {
-    TemporaryDir td;
-
-    // Generate Partition Image
-    TemporaryFile system_tf(std::string(td.path));
-    std::string system_path(system_tf.path);
-    GeneratePartitionImage(system_tf.fd, system_path, "system");
-    system_tf.release();
-
-    TemporaryFile vendor_tf(std::string(td.path));
-    std::string vendor_path(vendor_tf.path);
-    GeneratePartitionImage(vendor_tf.fd, vendor_path, "vendor");
-    vendor_tf.release();
-
-    TemporaryFile product_tf(std::string(td.path));
-    std::string product_path(product_tf.path);
-    GeneratePartitionImage(product_tf.fd, product_path, "product");
-    product_tf.release();
-
-    // Generate VBMeta Image
-    std::string vbmeta_system_path(td.path);
-    vbmeta_system_path.append("/vbmeta_system.img");
-    GenerateVBMetaImage(vbmeta_system_path, system_path);
-
-    std::string vbmeta_vendor_path(td.path);
-    vbmeta_vendor_path.append("/vbmeta_vendor.img");
-    GenerateVBMetaImage(vbmeta_vendor_path, vendor_path);
-
-    std::string vbmeta_product_path(td.path);
-    vbmeta_product_path.append("/vbmeta_product.img");
-    GenerateVBMetaImage(vbmeta_product_path, product_path);
-
-    // Generate Super VBMeta Image
-    std::string super_vbmeta_path(td.path);
-    super_vbmeta_path.append("/super_vbmeta.img");
-
-    std::stringstream cmd;
-    cmd << "vbmake"
-        << " --image "
-        << "vbmeta_system"
-        << "=" << vbmeta_system_path << " --image "
-        << "vbmeta_vendor"
-        << "=" << vbmeta_vendor_path << " --image "
-        << "vbmeta_product"
-        << "=" << vbmeta_product_path << " --output=" << super_vbmeta_path;
-
-    int rc = system(cmd.str().c_str());
-    ASSERT_TRUE(WIFEXITED(rc));
-    ASSERT_EQ(WEXITSTATUS(rc), 0);
-
-    android::base::unique_fd fd(open(super_vbmeta_path.c_str(), O_RDONLY | O_CLOEXEC));
-    EXPECT_GT(fd, 0);
-
-    // Check the size of vbmeta table
-    Result<uint64_t> super_vbmeta_size = GetFileSize(fd);
-    EXPECT_TRUE(super_vbmeta_size);
-    EXPECT_EQ(super_vbmeta_size.value(),
-              SUPER_VBMETA_TABLE_MAX_SIZE * 2 + VBMETA_IMAGE_MAX_SIZE * 3);
-
-    // Check Primary vbmeta table is equal to Backup one
-    VBMetaTable table;
-    EXPECT_TRUE(android::fs_mgr::ReadPrimaryVBMetaTable(fd, &table));
-    VBMetaTable table_backup;
-    EXPECT_TRUE(android::fs_mgr::ReadBackupVBMetaTable(fd, &table_backup));
-    EXPECT_EQ(android::fs_mgr::SerializeVBMetaTable(table),
-              android::fs_mgr::SerializeVBMetaTable(table_backup));
-
-    // Check vbmeta table Header Checksum
-    std::string serial_table = android::fs_mgr::SerializeVBMetaTable(table);
-    std::string serial_removed_checksum(serial_table);
-    // Replace checksum 32 bytes (starts at 16th byte) with 0
-    serial_removed_checksum.replace(16, 32, 32, 0);
-    uint8_t test_checksum[32];
-    ::SHA256(reinterpret_cast<const uint8_t*>(serial_removed_checksum.c_str()),
-             table.header.total_size, &test_checksum[0]);
-    EXPECT_EQ(memcmp(table.header.checksum, test_checksum, 32), 0);
-
-    // Check vbmeta table descriptors and vbmeta images
-    EXPECT_EQ(table.descriptors.size(), 3);
-
-    EXPECT_EQ(table.descriptors[0].vbmeta_index, 0);
-    EXPECT_EQ(table.descriptors[0].vbmeta_name_length, 14);
-    EXPECT_EQ(table.descriptors[0].vbmeta_name, "vbmeta_product");
-    Result<std::string> vbmeta_product_content = ReadVBMetaImage(fd, 0);
-    EXPECT_TRUE(vbmeta_product_content);
-    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_product_path), vbmeta_product_content.value());
-
-    EXPECT_EQ(table.descriptors[1].vbmeta_index, 1);
-    EXPECT_EQ(table.descriptors[1].vbmeta_name_length, 13);
-    EXPECT_EQ(table.descriptors[1].vbmeta_name, "vbmeta_system");
-    Result<std::string> vbmeta_system_content = ReadVBMetaImage(fd, 1);
-    EXPECT_TRUE(vbmeta_system_content);
-    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_system_path), vbmeta_system_content.value());
-
-    EXPECT_EQ(table.descriptors[2].vbmeta_index, 2);
-    EXPECT_EQ(table.descriptors[2].vbmeta_name_length, 13);
-    EXPECT_EQ(table.descriptors[2].vbmeta_name, "vbmeta_vendor");
-    Result<std::string> vbmeta_vendor_content = ReadVBMetaImage(fd, 2);
-    EXPECT_TRUE(vbmeta_vendor_content);
-    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_vendor_path), vbmeta_vendor_content.value());
-}
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    return RUN_ALL_TESTS();
-}
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/utility.cpp b/fs_mgr/libvbmeta/utility.cpp
deleted file mode 100644
index c184227..0000000
--- a/fs_mgr/libvbmeta/utility.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "super_vbmeta_format.h"
-
-using android::base::ErrnoError;
-using android::base::Error;
-using android::base::Result;
-
-namespace android {
-namespace fs_mgr {
-
-Result<uint64_t> GetFileSize(int fd) {
-    struct stat sb;
-    if (fstat(fd, &sb) == -1) {
-        return ErrnoError() << "Couldn't get the file size";
-    }
-    return sb.st_size;
-}
-
-uint64_t IndexOffset(const uint8_t vbmeta_index) {
-    /* There are primary and backup vbmeta table in super_vbmeta,
-       so SUPER_VBMETA_TABLE_MAX_SIZE is counted twice. */
-    return 2 * SUPER_VBMETA_TABLE_MAX_SIZE + vbmeta_index * VBMETA_IMAGE_MAX_SIZE;
-}
-
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/fs_mgr/libvbmeta/utility.h b/fs_mgr/libvbmeta/utility.h
deleted file mode 100644
index 91db0ad..0000000
--- a/fs_mgr/libvbmeta/utility.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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-base/logging.h>
-#include <android-base/result.h>
-
-#define VBMETA_TAG "[libvbmeta]"
-#define LWARN LOG(WARNING) << VBMETA_TAG
-#define LINFO LOG(INFO) << VBMETA_TAG
-#define LERROR LOG(ERROR) << VBMETA_TAG
-#define PWARNING PLOG(WARNING) << VBMETA_TAG
-#define PERROR PLOG(ERROR) << VBMETA_TAG
-
-namespace android {
-namespace fs_mgr {
-
-android::base::Result<uint64_t> GetFileSize(int fd);
-
-uint64_t IndexOffset(const uint8_t vbmeta_index);
-
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/writer.cpp b/fs_mgr/libvbmeta/writer.cpp
deleted file mode 100644
index fc0e0fb..0000000
--- a/fs_mgr/libvbmeta/writer.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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 "writer.h"
-
-#include <android-base/file.h>
-
-#include "utility.h"
-
-using android::base::ErrnoError;
-using android::base::Result;
-
-namespace android {
-namespace fs_mgr {
-
-std::string SerializeVBMetaTable(const VBMetaTable& input) {
-    std::string table;
-    table.append(reinterpret_cast<const char*>(&input.header), SUPER_VBMETA_HEADER_SIZE);
-
-    for (const auto& desc : input.descriptors) {
-        table.append(reinterpret_cast<const char*>(&desc), SUPER_VBMETA_DESCRIPTOR_SIZE);
-        table.append(desc.vbmeta_name);
-    }
-
-    // Ensure the size of vbmeta table is SUPER_VBMETA_TABLE_MAX_SIZE
-    table.resize(SUPER_VBMETA_TABLE_MAX_SIZE, '\0');
-
-    return table;
-}
-
-Result<void> WritePrimaryVBMetaTable(int fd, const std::string& table) {
-    const uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET;
-    if (lseek(fd, offset, SEEK_SET) < 0) {
-        return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
-    }
-
-    if (!android::base::WriteFully(fd, table.data(), table.size())) {
-        return ErrnoError() << "Failed to write primary vbmeta table at offset " << offset;
-    }
-    return {};
-}
-
-Result<void> WriteBackupVBMetaTable(int fd, const std::string& table) {
-    const uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET;
-    if (lseek(fd, offset, SEEK_SET) < 0) {
-        return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
-    }
-
-    if (!android::base::WriteFully(fd, table.data(), table.size())) {
-        return ErrnoError() << "Failed to write backup vbmeta table at offset " << offset;
-    }
-    return {};
-}
-
-Result<void> WriteVBMetaImage(int fd, const uint8_t slot_number, const std::string& vbmeta_image) {
-    const uint64_t offset = IndexOffset(slot_number);
-    if (lseek(fd, offset, SEEK_SET) < 0) {
-        return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
-    }
-
-    if (!android::base::WriteFully(fd, vbmeta_image.data(), vbmeta_image.size())) {
-        return ErrnoError() << "Failed to write vbmeta image at offset " << offset;
-    }
-    return {};
-}
-
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/writer.h b/fs_mgr/libvbmeta/writer.h
deleted file mode 100644
index f8eed36..0000000
--- a/fs_mgr/libvbmeta/writer.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 <android-base/result.h>
-
-#include "super_vbmeta_format.h"
-
-namespace android {
-namespace fs_mgr {
-
-std::string SerializeVBMetaTable(const VBMetaTable& input);
-
-android::base::Result<void> WritePrimaryVBMetaTable(int fd, const std::string& table);
-android::base::Result<void> WriteBackupVBMetaTable(int fd, const std::string& table);
-android::base::Result<void> WriteVBMetaImage(int fd, const uint8_t slot_number,
-                                             const std::string& vbmeta_image);
-
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 4f6ec5a..eb9f525 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -13,21 +13,8 @@
 // limitations under the License.
 
 cc_test {
-    name: "CtsFsMgrTestCases",
-    test_suites: [
-        "cts",
-        "device-tests",
-        "vts",
-    ],
-    compile_multilib: "both",
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
-        },
-    },
+    name: "fs_mgr_unit_test",
+    test_suites: ["device-tests"],
 
     shared_libs: [
         "libbase",
@@ -38,7 +25,6 @@
         "libfstab",
     ],
     srcs: [
-        "file_wait_test.cpp",
         "fs_mgr_test.cpp",
     ],
 
diff --git a/fs_mgr/tests/AndroidTest.xml b/fs_mgr/tests/AndroidTest.xml
deleted file mode 100644
index 91c3fb9..0000000
--- a/fs_mgr/tests/AndroidTest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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 CTS fs_mgr 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="CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases" />
-        <option name="append-bitness" value="true" />
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="CtsFsMgrTestCases" />
-        <option name="runtime-hint" value="65s" />
-    </test>
-</configuration>
diff --git a/fs_mgr/tests/adb-remount-sh.xml b/fs_mgr/tests/adb-remount-sh.xml
index fa0d63f..716e324 100644
--- a/fs_mgr/tests/adb-remount-sh.xml
+++ b/fs_mgr/tests/adb-remount-sh.xml
@@ -18,8 +18,6 @@
     <!-- 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
index 4226e95..c22176b 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -15,13 +15,10 @@
 
 adb remount tests
 
---color                     Dress output with highlighting colors
---help                      This help
---no-wait-screen            Do not wait for display screen to settle
---print-time                Report the test duration
---serial                    Specify device (must if multiple are present)
---wait-adb <duration>       adb wait timeout
---wait-fastboot <duration>  fastboot wait timeout
+--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.
@@ -36,10 +33,7 @@
 ##  Helper Variables
 ##
 
-EMPTY=""
 SPACE=" "
-# Line up wrap to [  XXXXXXX ] messages.
-INDENT="             "
 # A _real_ embedded tab character
 TAB="`echo | tr '\n' '\t'`"
 # A _real_ embedded escape character
@@ -56,10 +50,6 @@
 start_time=`date +%s`
 ACTIVE_SLOT=
 
-ADB_WAIT=4m
-FASTBOOT_WAIT=2m
-screen_wait=true
-
 ##
 ##  Helper Functions
 ##
@@ -141,29 +131,10 @@
 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/^/${INDENT}/" >&2
-}
-
 [ "USAGE: get_property <prop>
 
 Returns the property value" ]
@@ -190,7 +161,7 @@
 [ "USAGE: adb_cat <file> >stdout
 
 Returns: content of file to stdout with carriage returns skipped,
-         true if the file exists" ]
+         true of the file exists" ]
 adb_cat() {
     local OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
     local ret=${?}
@@ -198,23 +169,11 @@
     return ${ret}
 }
 
-[ "USAGE: adb_ls <dirfile> >stdout
-
-Returns: filename or directoru content to stdout with carriage returns skipped,
-         true if the ls had no errors" ]
-adb_ls() {
-    local OUTPUT="`adb_sh ls ${1} </dev/null 2>/dev/null`"
-    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
+  adb reboot remount-test || true
   sleep 2
 }
 
@@ -260,34 +219,13 @@
   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}"
+    echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
     timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
     ret=${?}
     echo -n "                                                                             ${CR}"
@@ -295,83 +233,30 @@
     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
+[ "USAGE: usb_status > stdout
 
-Returns: the adb daemon user" ]
-adb_user() {
-  adb_sh echo '${USER}' </dev/null
-}
+If adb_wait failed, check if device is in adb, recovery or fastboot mode
+and report status string.
 
-[ "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" ]
+Returns: \"(USB stack borken?)\", \"(In fastboot mode)\" or \"(in adb mode)\"" ]
 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`)"
+    echo "(In adb mode)"
   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
+    echo "(USB stack borken?)"
   fi
 }
 
@@ -383,8 +268,7 @@
   # 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}"
+    echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
     timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null
     ret=${?}
     echo -n "                                                                             ${CR}"
@@ -394,12 +278,11 @@
   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
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
   fi
   return ${ret}
 }
@@ -410,8 +293,7 @@
 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}"
+    echo -n ". . . waiting `format_duration ${1}`" ${ANDROID_SERIAL} ${USB_ADDRESS} "${CR}"
     timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null
     ret=${?}
     echo -n "                                                                             ${CR}"
@@ -419,12 +301,11 @@
     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
+      echo "${ORANGE}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+    fi
   fi
   return ${ret}
 }
@@ -446,72 +327,17 @@
   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() {
-  if ! ${screen_wait}; then
-    adb_wait
-    return
-  fi
-  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
+  [ root != "`adb_sh echo '${USER}' </dev/null`" ] || return 0
   adb root >/dev/null </dev/null 2>/dev/null
   sleep 2
-  adb_wait ${ADB_WAIT} &&
-    [ root = "`adb_user`" ]
+  adb_wait 2m &&
+    [ root = "`adb_sh echo '${USER}' </dev/null`" ]
 }
 
 [ "USAGE: adb_unroot
@@ -520,11 +346,11 @@
 
 Returns: true if device in un root state" ]
 adb_unroot() {
-  [ root = "`adb_user`" ] || return 0
+  [ root = "`adb_sh echo '${USER}' </dev/null`" ] || return 0
   adb unroot >/dev/null </dev/null 2>/dev/null
   sleep 2
-  adb_wait ${ADB_WAIT} &&
-    [ root != "`adb_user`" ]
+  adb_wait 2m &&
+    [ root != "`adb_sh echo '${USER}' </dev/null`" ]
 }
 
 [ "USAGE: fastboot_getvar var expected >/dev/stderr
@@ -544,10 +370,10 @@
     O="${1}: <empty>"
   fi
   if [ -n "${2}" -a "${1}: ${2}" != "${O}" ]; then
-    echo "${2} != ${O}"
+    echo "${2} != ${O}" >&2
     false
     return
-  fi >&2
+  fi
   echo ${O} >&2
 }
 
@@ -604,16 +430,16 @@
 Returns: exit failure, report status" ]
 die() {
   if [ X"-d" = X"${1}" ]; then
-    adb_logcat -b all -v nsec -d
+    adb_logcat -b all -v nsec -d >&2
     shift
   elif [ X"-t" = X"${1}" ]; then
     if [ -n "${2}" ]; then
-      adb_logcat -b all -v nsec -t ${2}
+      adb_logcat -b all -v nsec -t ${2} >&2
     else
-      adb_logcat -b all -v nsec -d
+      adb_logcat -b all -v nsec -d >&2
     fi
     shift 2
-  fi >&2
+  fi
   echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
   cleanup
   restore
@@ -638,63 +464,39 @@
   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} expected \"${lval}\"" >&2
       echo "${prefix} got \"${rval}\"" |
-        sed ": again
+        sed ': again
              N
-             s/\(\n\)\([^ ]\)/\1${INDENT}\2/
-             t again"
+             s/\(\n\)\([^ ]\)/\1             \2/
+             t again' >&2
       if [ -n "${*}" ] ; then
-        echo "${prefix} ${*}"
+        echo "${prefix} ${*}" >&2
       fi
     else
-      echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}"
-    fi >&2
+      echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}" >&2
+    fi
     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 "${prefix} ok \"${lval}\"" >&2
         echo "       = \"${rval}\"" |
-          sed ": again
+          sed ': again
                N
-               s/\(\n\)\([^ ]\)/\1${INDENT}\2/
-               t again"
+               s/\(\n\)\([^ ]\)/\1          \2/
+               t again' >&2
         if [ -n "${*}" ] ; then
-          echo "${prefix} ${*}"
+          echo "${prefix} ${*}" >&2
         fi
       else
-        echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}"
+        echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}" >&2
       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
+      echo "${prefix} ok \"${lval}\" ${*}" >&2
+    fi
   fi
   return 0
 }
@@ -710,25 +512,8 @@
       EXPECT_EQ "${lval}" "${rval}" ${*}
       return
   fi
-  if ! EXPECT_EQ "${lval}" "${rval}"; then
+  EXPECT_EQ "${lval}" "${rval}" ||
     die "${@}"
-  fi
-}
-
-[ "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
-  if ! EXPECT_NE "${lval}" "${rval}"; then
-    die "${@}"
-  fi
 }
 
 [ "USAGE: skip_administrative_mounts [data] < /proc/mounts
@@ -765,9 +550,6 @@
 
 OPTIONS=`getopt --alternative --unquoted \
                 --longoptions help,serial:,colour,color,no-colour,no-color \
-                --longoptions wait-adb:,wait-fastboot: \
-                --longoptions wait-screen,wait-display \
-                --longoptions no-wait-screen,no-wait-display \
                 --longoptions gtest_print_time,print-time \
                 -- "?hs:" ${*}` ||
   ( echo "${USAGE}" >&2 ; false ) ||
@@ -791,23 +573,9 @@
     --no-color | --no-colour)
       color=false
       ;;
-    --no-wait-display | --no-wait-screen)
-      screen_wait=false
-      ;;
-    --wait-display | --wait-screen)
-      screen_wait=true
-      ;;
     --print-time | --gtest_print_time)
       print_time=true
       ;;
-    --wait-adb)
-      ADB_WAIT=${2}
-      shift
-      ;;
-    --wait-fastboot)
-      FASTBOOT_WAIT=${2}
-      shift
-      ;;
     --)
       shift
       break
@@ -838,7 +606,7 @@
 inRecovery && die "device in recovery mode"
 if ! inAdb; then
   echo "${ORANGE}[  WARNING ]${NORMAL} device not in adb mode" >&2
-  adb_wait ${ADB_WAIT}
+  adb_wait 2m
 fi
 inAdb || die "specified device not in adb mode"
 isDebuggable || die "device not a debug build"
@@ -850,8 +618,6 @@
 
 # Do something.
 
-# Collect characteristics of the device and report.
-
 D=`get_property ro.serialno`
 [ -n "${D}" ] || D=`get_property ro.boot.serialno`
 [ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
@@ -865,8 +631,7 @@
   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
+  echo "${BLUE}[     INFO ]${NORMAL}" ${ANDROID_SERIAL} ${USB_ADDRESS} >&2
 BUILD_DESCRIPTION=`get_property ro.build.description`
 [ -z "${BUILD_DESCRIPTION}" ] ||
   echo "${BLUE}[     INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
@@ -874,106 +639,49 @@
 [ -z "${ACTIVE_SLOT}" ] ||
   echo "${BLUE}[     INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
 
-# Acquire list of system partitions
-
-PARTITIONS=`adb_su cat /vendor/etc/fstab* |
-              skip_administrative_mounts |
-              sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
-              sort -u |
-              tr '\n' ' '`
-PARTITIONS="${PARTITIONS:-system vendor}"
-# KISS (we do not support sub-mounts for system partitions currently)
-MOUNTS="`for i in ${PARTITIONS}; do
-           echo /${i}
-         done |
-         tr '\n' ' '`"
-echo "${BLUE}[     INFO ]${NORMAL} System Partitions list: ${PARTITIONS}" >&2
-
 # Report existing partition sizes
-adb_sh ls -l /dev/block/by-name/ /dev/block/mapper/ </dev/null 2>/dev/null |
+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
-    [ super = ${name} -o cache = ${name} ] ||
-      (
-        for i in ${PARTITIONS}; do
-          [ ${i} = ${name} -o ${i} = ${name%_[ab]} ] && exit
-        done
-        exit 1
-      ) ||
-      continue
-
-    case ${device} in
-      sd*)
-        device=${device%%[0-9]*}/${device}
+    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
-    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
   done
 
-# If reboot too soon after fresh flash, could trip device update failure logic
-wait_for_screen
 # Can we test remount -R command?
-OVERLAYFS_BACKING="cache mnt/scratch"
 overlayfs_supported=true
-if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
-     "2" != "`get_property partition.system.verified`" ]; then
+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} ||
-      true
-    if inAdb; then
-      reboot=false
-      for d in ${OVERLAYFS_BACKING}; do
-        if adb_su ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
-          adb_su rm -rf /${d}/overlay </dev/null
-          reboot=true
-        fi
-      done
-      if ${reboot}; then
-        adb_reboot &&
-        adb_wait ${ADB_WAIT}
-      fi
-    fi
-  }
-else
-  restore() {
-    ${overlayfs_supported} || return 0
-    inFastboot &&
-      fastboot reboot &&
-      adb_wait ${ADB_WAIT} ||
-      true
+      adb_wait 2m
     inAdb &&
       adb_root &&
       adb enable-verity >/dev/null 2>/dev/null &&
       adb_reboot &&
-      adb_wait ${ADB_WAIT}
+      adb_wait 2m
   }
 
   echo "${GREEN}[ RUN      ]${NORMAL} Testing adb shell su root remount -R command" >&2
 
-  avc_check
-  T=`adb_date`
-  adb_su remount -R system </dev/null
-  err=${?}
-  if [ "${err}" != 0 ]; then
-    echo "${ORANGE}[  WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
-    T="-t ${T}"
-  else
-    # Rebooted, logcat will be meaningless, and last logcat will likely be clear
-    T=""
-  fi
+  adb_su remount -R system </dev/null || true
   sleep 2
-  adb_wait ${ADB_WAIT} ||
-    die "waiting for device after adb shell su root remount -R system `usb_status`"
+  adb_wait 2m ||
+    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 ${T} "remount -R command failed
-${INDENT}ro.boot.verifiedbootstate=\"`get_property ro.boot.verifiedbootstate`\"
-${INDENT}partition.system.verified=\"`get_property partition.system.verified`\""
+    die "remount -R command failed"
   fi
 
   echo "${GREEN}[       OK ]${NORMAL} adb shell su root remount -R command" >&2
@@ -1015,6 +723,7 @@
 # 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
@@ -1026,7 +735,7 @@
 if ${reboot}; then
   echo "${ORANGE}[  WARNING ]${NORMAL} rebooting before test" >&2
   adb_reboot &&
-    adb_wait ${ADB_WAIT} ||
+    adb_wait 2m ||
     die "lost device after reboot after wipe `usb_status`"
   adb_root ||
     die "lost device after elevation to root after wipe `usb_status`"
@@ -1057,15 +766,6 @@
 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
@@ -1100,7 +800,7 @@
   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} ||
+    adb_wait 2m ||
     die "lost device after reboot requested `usb_status`"
   adb_root ||
     die "lost device after elevation to root `usb_status`"
@@ -1141,11 +841,6 @@
 
 echo "${GREEN}[ RUN      ]${NORMAL} remount" >&2
 
-# Feed log with selinux denials as baseline before overlays
-adb_unroot
-adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
-adb_root
-
 D=`adb remount 2>&1`
 ret=${?}
 echo "${D}"
@@ -1202,11 +897,6 @@
     skip_unrelated_mounts |
     grep " overlay ro,") ||
     die "remount overlayfs missed a spot (ro)"
-  !(adb_sh grep -v noatime /proc/mounts </dev/null |
-    skip_administrative_mounts data |
-    skip_unrelated_mounts |
-    grep -v ' ro,') ||
-    die "mounts are not noatime"
   D=`adb_sh grep " rw," /proc/mounts </dev/null |
      skip_administrative_mounts data`
   if echo "${D}" | grep /dev/root >/dev/null; then
@@ -1240,39 +930,17 @@
 
 # Check something.
 
-echo "${GREEN}[ RUN      ]${NORMAL} push content to ${MOUNTS}" >&2
+echo "${GREEN}[ RUN      ]${NORMAL} push content to /system and /vendor" >&2
 
 A="Hello World! $(date)"
-for i in ${MOUNTS}; do
-  echo "${A}" | adb_sh cat - ">${i}/hello"
-  B="`adb_cat ${i}/hello`" ||
-    die "${i#/} hello"
-  check_eq "${A}" "${B}" ${i} before reboot
-done
-echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
-B="`adb_cat /system/priv-app/hello`" ||
-  die "system priv-app hello"
-check_eq "${A}" "${B}" /system/priv-app 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"
+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
 
 # Download libc.so, append some gargage, push back, and check if the file
 # is updated.
@@ -1293,16 +961,8 @@
 
 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 ||
+  adb_wait 2m ||
   die "reboot after override content added failed `usb_status`"
 
 if ${overlayfs_needed}; then
@@ -1325,36 +985,16 @@
   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 ${MOUNTS} </dev/null >/dev/null 2>/dev/null
 fi
-# If overlayfs has a nested security problem, this will fail.
-B="`adb_ls /system/`" ||
-  die "adb ls /system"
-[ X"${B}" != X"${B#*priv-app}" ] ||
-  die "adb ls /system/priv-app"
-B="`adb_cat /system/priv-app/hello`"
-check_eq "${A}" "${B}" /system/priv-app after reboot
+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"
-for i in ${MOUNTS}; do
-  B="`adb_cat ${i}/hello`"
-  check_eq "${A}" "${B}" ${i#/} after reboot
-  echo "${GREEN}[       OK ]${NORMAL} ${i} content remains after reboot" >&2
-done
-
-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 ${MOUNTS} </dev/null >/dev/null 2>/dev/null
+B="`adb_cat /vendor/hello`"
+check_eq "${A}" "${B}" vendor after reboot
+echo "${GREEN}[       OK ]${NORMAL} /vendor content remains after reboot" >&2
 
 # Check if the updated libc.so is persistent after reboot.
 adb_root &&
@@ -1385,17 +1025,10 @@
   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 ||
+  adb reboot fastboot ||
     die "fastbootd not supported (wrong adb in path?)"
-  any_wait ${ADB_WAIT} &&
+  any_wait 2m &&
     inFastboot ||
     die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
   fastboot flash vendor ||
@@ -1436,9 +1069,8 @@
   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`"
+  adb_wait 2m ||
+    die "did not reboot after flash `usb_status`"
   if ${overlayfs_needed}; then
     adb_root &&
       D=`adb_sh df -k </dev/null` &&
@@ -1458,12 +1090,6 @@
   fi
   B="`adb_cat /system/hello`"
   check_eq "${A}" "${B}" system after flash vendor
-  B="`adb_ls /system/`" ||
-    die "adb ls /system"
-  [ X"${B}" != X"${B#*priv-app}" ] ||
-    die "adb ls /system/priv-app"
-  B="`adb_cat /system/priv-app/hello`"
-  check_eq "${A}" "${B}" system/priv-app after flash vendor
   adb_root ||
     die "adb root"
   B="`adb_cat /vendor/hello`"
@@ -1475,15 +1101,8 @@
     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`
@@ -1495,7 +1114,7 @@
   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_wait 2m &&
     adb_root ||
     die "failed to reboot"
   T=`adb_date`
@@ -1505,33 +1124,27 @@
 echo "${H}"
 [ ${err} = 0 ] &&
   ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
-  adb_sh rm /system/hello /system/priv-app/hello </dev/null ||
+  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 /system/priv-app/hello`"
-check_eq "cat: /system/priv-app/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
-for i in ${MOUNTS}; do
-  adb_sh rm ${i}/hello </dev/null 2>/dev/null || true
-done
 
-if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
+if [ -n "${scratch_partition}" ]; then
 
   echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
 
-  avc_check
-  adb reboot fastboot </dev/null ||
+  adb reboot fastboot ||
     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 to flash scratch `usb_status`"
+    fastboot_wait 2m ||
+    die "reboot into fastboot `usb_status`"
   fastboot flash --force ${scratch_partition} ${img}
   err=${?}
   cleanup
@@ -1542,9 +1155,9 @@
     die "can not reboot out of fastboot"
   [ 0 -eq ${err} ] ||
     die "fastboot flash ${scratch_partition}"
-  adb_wait ${ADB_WAIT} &&
+  adb_wait 2m &&
     adb_root ||
-    die "did not reboot after flashing empty ${scratch_partition} `usb_status`"
+    die "did not reboot after flash"
   T=`adb_date`
   D=`adb disable-verity 2>&1`
   err=${?}
@@ -1552,7 +1165,7 @@
   then
     echo "${ORANGE}[  WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
     adb_reboot &&
-      adb_wait ${ADB_WAIT} &&
+      adb_wait 2m &&
       adb_root ||
       die "failed to reboot"
     T=`adb_date`
@@ -1578,25 +1191,9 @@
 
 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 ||
+  adb_wait 2m ||
   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"
@@ -1608,9 +1205,8 @@
 
 # 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_wait 2m ||
+  die "lost device after reboot to ro state (USB stack broken?)"
 adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
   die "/vendor is not read-only"
 adb_su remount vendor </dev/null ||
@@ -1629,50 +1225,30 @@
     die "/${d}/overlay wipe"
 done
 adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
-  fixup_from_fastboot ||
-  die "lost device after reboot after wipe `usb_status`"
+  adb_wait 2m ||
+  die "lost device after reboot after wipe (USB stack broken?)"
 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 &&
+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
+restore
+err=${?}
 
-err=0
-
-if ${overlayfs_supported}; then
+if [ ${err} = 0 ] && ${overlayfs_supported}; then
   echo "${GREEN}[ RUN      ]${NORMAL} test 'adb remount -R'" >&2
-  avc_check
-  adb_root ||
-    die "adb root in preparation for adb remount -R"
-  T=`adb_date`
-  adb remount -R
-  err=${?}
-  if [ "${err}" != 0 ]; then
-    die -t ${T} "adb remount -R = ${err}"
-  fi
-  sleep 2
-  adb_wait ${ADB_WAIT} ||
-    die "waiting for device after adb remount -R `usb_status`"
+  adb_root &&
+    adb remount -R &&
+    adb_wait 2m ||
+    die "adb remount -R"
   if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
-       "2" = "`get_property partition.system.verified`" ] &&
-     [ -n "`get_property ro.boot.verifiedbootstate`" -o \
-       -n "`get_property partition.system.verified`" ]; then
-    die "remount -R command failed to disable verity
-${INDENT}ro.boot.verifiedbootstate=\"`get_property ro.boot.verifiedbootstate`\"
-${INDENT}partition.system.verified=\"`get_property partition.system.verified`\""
+       "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
@@ -1686,7 +1262,7 @@
 }
 
 [ ${err} = 0 ] ||
-  die "failed to restore verity"
+  die "failed to restore verity" >&2
 
 echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
 
diff --git a/fs_mgr/tests/file_wait_test.cpp b/fs_mgr/tests/file_wait_test.cpp
deleted file mode 100644
index cc8b143..0000000
--- a/fs_mgr/tests/file_wait_test.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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
index 1cbaf45..72afa69 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -179,7 +179,6 @@
                 {"nodiratime", MS_NODIRATIME},
                 {"ro", MS_RDONLY},
                 {"rw", 0},
-                {"sync", MS_SYNCHRONOUS},
                 {"remount", MS_REMOUNT},
                 {"bind", MS_BIND},
                 {"rec", MS_REC},
@@ -198,7 +197,7 @@
         if (!(entry.flags & MS_RDONLY)) {
             fs_options.emplace("rw");
         }
-        EXPECT_EQ(mnt_opts, fs_options) << "At line " << i;
+        EXPECT_EQ(mnt_opts, fs_options);
         ++i;
     }
     EXPECT_EQ(i, fstab.size());
@@ -395,7 +394,7 @@
     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 none1       swap   defaults      encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,verify=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_loopback_path=,zram_loopback_size=,zram_backing_dev_path=
 
 source none2       swap   defaults      forcefdeorfbe=
 
@@ -414,6 +413,7 @@
     }
     EXPECT_EQ("", entry->key_loc);
     EXPECT_EQ("", entry->key_dir);
+    EXPECT_EQ("", entry->verity_loc);
     EXPECT_EQ(0, entry->length);
     EXPECT_EQ("", entry->label);
     EXPECT_EQ(-1, entry->partnum);
@@ -421,7 +421,8 @@
     EXPECT_EQ(0, entry->max_comp_streams);
     EXPECT_EQ(0, entry->zram_size);
     EXPECT_EQ(0, entry->reserved_size);
-    EXPECT_EQ("", entry->encryption_options);
+    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);
@@ -436,11 +437,13 @@
         flags.crypt = true;
         flags.force_crypt = true;
         flags.file_encryption = true;
+        flags.verify = true;
         flags.avb = true;
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
     EXPECT_EQ("", entry->key_loc);
     EXPECT_EQ("", entry->key_dir);
+    EXPECT_EQ("", entry->verity_loc);
     EXPECT_EQ(0, entry->length);
     EXPECT_EQ("", entry->label);
     EXPECT_EQ(-1, entry->partnum);
@@ -448,7 +451,8 @@
     EXPECT_EQ(0, entry->max_comp_streams);
     EXPECT_EQ(0, entry->zram_size);
     EXPECT_EQ(0, entry->reserved_size);
-    EXPECT_EQ("", entry->encryption_options);
+    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);
@@ -457,14 +461,15 @@
     EXPECT_EQ("", entry->zram_backing_dev_path);
     entry++;
 
-    // forcefdeorfbe has its own encryption_options defaults, so test it separately.
+    // 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:aes-256-cts", entry->encryption_options);
+    EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
+    EXPECT_EQ("aes-256-cts", entry->file_names_mode);
     EXPECT_EQ("", entry->key_loc);
 }
 
@@ -634,6 +639,29 @@
     EXPECT_EQ(0, entry->zram_size);
 }
 
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Verify) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      verify=/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.verify = true;
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+    EXPECT_EQ("/dir/key", entry->verity_loc);
+}
+
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceEncrypt) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
@@ -678,21 +706,32 @@
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
 
     EXPECT_EQ("/dir/key", entry->key_loc);
-    EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
+    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=aes-256-xts:aes-256-cts:v1
+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(1U, fstab.size());
+    ASSERT_EQ(11U, fstab.size());
 
     FstabEntry::FsMgrFlags flags = {};
     flags.file_encryption = true;
@@ -700,7 +739,68 @@
     auto entry = fstab.begin();
     EXPECT_EQ("none0", entry->mount_point);
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ("aes-256-xts:aes-256-cts:v1", entry->encryption_options);
+    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) {
@@ -962,10 +1062,3 @@
     EXPECT_EQ("none5", entry->mount_point);
     EXPECT_EQ("/dev/path2", entry->zram_backing_dev_path);
 }
-
-TEST(fs_mgr, DefaultFstabContainsUserdata) {
-    Fstab fstab;
-    ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
-    ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, "/data"))
-            << "Default fstab doesn't contain /data entry";
-}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 2738457..9309aad 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,7 +38,15 @@
 #include <vector>
 
 using namespace std::literals::string_literals;
-using namespace android::dm;
+
+using DeviceMapper = ::android::dm::DeviceMapper;
+using DmTable = ::android::dm::DmTable;
+using DmTarget = ::android::dm::DmTarget;
+using DmTargetLinear = ::android::dm::DmTargetLinear;
+using DmTargetZero = ::android::dm::DmTargetZero;
+using DmTargetAndroidVerity = ::android::dm::DmTargetAndroidVerity;
+using DmTargetBow = ::android::dm::DmTargetBow;
+using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
 
 static int Usage(void) {
@@ -49,10 +57,6 @@
     std::cerr << "  delete <dm-name>" << std::endl;
     std::cerr << "  list <devices | targets> [-v]" << std::endl;
     std::cerr << "  getpath <dm-name>" << std::endl;
-    std::cerr << "  info <dm-name>" << std::endl;
-    std::cerr << "  status <dm-name>" << std::endl;
-    std::cerr << "  resume <dm-name>" << std::endl;
-    std::cerr << "  suspend <dm-name>" << std::endl;
     std::cerr << "  table <dm-name>" << std::endl;
     std::cerr << "  help" << std::endl;
     std::cerr << std::endl;
@@ -118,62 +122,6 @@
             }
             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;
@@ -197,12 +145,19 @@
     char** argv_;
 };
 
-static bool parse_table_args(DmTable* table, 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);
+            table.set_readonly(true);
             arg_index++;
         } else {
             std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
@@ -214,30 +169,15 @@
     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))) {
+        if (!target || !table.AddTarget(std::move(target))) {
             return -EINVAL;
         }
     }
 
-    if (table->num_targets() == 0) {
+    if (table.num_targets() == 0) {
         std::cerr << "Must define at least one target." << std::endl;
         return -EINVAL;
     }
-    return 0;
-}
-
-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];
-
-    DmTable table;
-    int ret = parse_table_args(&table, argc, argv);
-    if (ret) {
-        return ret;
-    }
 
     DeviceMapper& dm = DeviceMapper::Instance();
     if (!dm.CreateDevice(name, table)) {
@@ -263,27 +203,6 @@
     return 0;
 }
 
-static int DmReplaceCmdHandler(int argc, char** argv) {
-    if (argc < 1) {
-        std::cerr << "Usage: dmctl replace <dm-name> <targets...>" << std::endl;
-        return -EINVAL;
-    }
-    std::string name = argv[0];
-
-    DmTable table;
-    int ret = parse_table_args(&table, argc, argv);
-    if (ret) {
-        return ret;
-    }
-
-    DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.LoadTableAndActivate(name, table)) {
-        std::cerr << "Failed to replace device-mapper table to: " << name << std::endl;
-        return -EIO;
-    }
-    return 0;
-}
-
 static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
                          [[maybe_unused]] char** argv) {
     std::vector<DmTargetTypeInfo> targets;
@@ -389,42 +308,7 @@
     return 0;
 }
 
-static int InfoCmdHandler(int argc, char** argv) {
-    if (argc != 1) {
-        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
-        return -EINVAL;
-    }
-
-    DeviceMapper& dm = DeviceMapper::Instance();
-    auto info = dm.GetDetailedInfo(argv[0]);
-    if (!info) {
-        std::cerr << "Invalid device \"" << argv[0] << "\"." << std::endl;
-        return -EINVAL;
-    }
-
-    constexpr int spacing = 14;
-    std::cout << std::left << std::setw(spacing) << "device"
-              << ": " << argv[0] << std::endl;
-    std::cout << std::left << std::setw(spacing) << "active"
-              << ": " << std::boolalpha << !info->IsSuspended() << std::endl;
-    std::cout << std::left << std::setw(spacing) << "access"
-              << ": ";
-    if (info->IsReadOnly()) {
-        std::cout << "ro ";
-    } else {
-        std::cout << "rw ";
-    }
-    std::cout << std::endl;
-    std::cout << std::left << std::setw(spacing) << "activeTable"
-              << ": " << std::boolalpha << info->IsActiveTablePresent() << std::endl;
-    std::cout << std::left << std::setw(spacing) << "inactiveTable"
-              << ": " << std::boolalpha << info->IsInactiveTablePresent() << std::endl;
-    std::cout << std::left << std::setw(spacing) << "bufferFull"
-              << ": " << std::boolalpha << info->IsBufferFull() << std::endl;
-    return 0;
-}
-
-static int DumpTable(const std::string& mode, int argc, char** argv) {
+static int TableCmdHandler(int argc, char** argv) {
     if (argc != 1) {
         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
         return -EINVAL;
@@ -432,18 +316,9 @@
 
     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;
-        }
+    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) {
@@ -458,55 +333,14 @@
     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 int ResumeCmdHandler(int argc, char** argv) {
-    if (argc != 1) {
-        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
-        return -EINVAL;
-    }
-
-    DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.ChangeState(argv[0], DmDeviceState::ACTIVE)) {
-        std::cerr << "Could not resume device \"" << argv[0] << "\"." << std::endl;
-        return -EINVAL;
-    }
-    return 0;
-}
-
-static int SuspendCmdHandler(int argc, char** argv) {
-    if (argc != 1) {
-        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
-        return -EINVAL;
-    }
-
-    DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.ChangeState(argv[0], DmDeviceState::SUSPENDED)) {
-        std::cerr << "Could not suspend device \"" << argv[0] << "\"." << std::endl;
-        return -EINVAL;
-    }
-    return 0;
-}
-
 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
         // clang-format off
         {"create", DmCreateCmdHandler},
         {"delete", DmDeleteCmdHandler},
-        {"replace", DmReplaceCmdHandler},
         {"list", DmListCmdHandler},
         {"help", HelpCmdHandler},
         {"getpath", GetPathCmdHandler},
-        {"info", InfoCmdHandler},
         {"table", TableCmdHandler},
-        {"status", StatusCmdHandler},
-        {"resume", ResumeCmdHandler},
-        {"suspend", SuspendCmdHandler},
         // clang-format on
 };
 
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 27a6452..2b7db79 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -23,6 +23,8 @@
         "-Wunused",
     ],
     srcs: [
+        "SoftGateKeeperDevice.cpp",
+        "IGateKeeperService.cpp",
         "gatekeeperd.cpp",
     ],
 
@@ -38,45 +40,12 @@
         "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/GateKeeperResponse.cpp b/gatekeeperd/GateKeeperResponse.cpp
deleted file mode 100644
index ca0c98f..0000000
--- a/gatekeeperd/GateKeeperResponse.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-**
-** 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
new file mode 100644
index 0000000..43d5708
--- /dev/null
+++ b/gatekeeperd/IGateKeeperService.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 OK;
+        }
+        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 OK;
+        }
+        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 OK;
+        }
+        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 OK;
+        }
+        case CLEAR_SECURE_USER_ID: {
+            CHECK_INTERFACE(IGateKeeperService, data, reply);
+            uint32_t uid = data.readInt32();
+            clearSecureUserId(uid);
+            reply->writeNoException();
+            return OK;
+        }
+        case REPORT_DEVICE_SETUP_COMPLETE: {
+            CHECK_INTERFACE(IGateKeeperService, data, reply);
+            reportDeviceSetupComplete();
+            reply->writeNoException();
+            return OK;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+};
+
+
+}; // namespace android
diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h
new file mode 100644
index 0000000..2816efc
--- /dev/null
+++ b/gatekeeperd/IGateKeeperService.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.
+ */
+
+#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
new file mode 100644
index 0000000..5c03dcf
--- /dev/null
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -0,0 +1,175 @@
+/*
+ * 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;
+        *auth_token_key = key_.get();
+        *length = SIGNATURE_LENGTH_BYTES;
+        return true;
+    }
+
+    virtual void GetPasswordKey(const uint8_t** password_key, uint32_t* length) {
+        if (password_key == NULL || length == NULL) return;
+        *password_key = key_.get();
+        *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
new file mode 100644
index 0000000..f5e2ce6
--- /dev/null
+++ b/gatekeeperd/SoftGateKeeperDevice.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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
new file mode 100644
index 0000000..e3dc068
--- /dev/null
+++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -0,0 +1,76 @@
+/*
+ * 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/IGateKeeperService.aidl b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
deleted file mode 100644
index 57adaba..0000000
--- a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
+++ /dev/null
@@ -1,87 +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.
- */
-
-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 1d65b1c..5451819 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -16,8 +16,7 @@
 
 #define LOG_TAG "gatekeeperd"
 
-#include <android/service/gatekeeper/BnGateKeeperService.h>
-#include <gatekeeper/GateKeeperResponse.h>
+#include "IGateKeeperService.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -42,6 +41,8 @@
 #include <utils/Log.h>
 #include <utils/String16.h>
 
+#include "SoftGateKeeperDevice.h"
+
 #include <hidl/HidlSupport.h>
 #include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
 
@@ -51,11 +52,6 @@
 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");
@@ -68,8 +64,9 @@
         hw_device = IGatekeeper::getService();
         is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
 
-        if (!hw_device) {
-            LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
+        if (hw_device == nullptr) {
+            ALOGW("falling back to software GateKeeper");
+            soft_device.reset(new SoftGateKeeperDevice());
         }
     }
 
@@ -95,7 +92,7 @@
 
         if (mark_cold_boot() && !is_running_gsi) {
             ALOGI("cold boot: clearing state");
-            if (hw_device) {
+            if (hw_device != nullptr) {
                 hw_device->deleteAllUsers([](const GatekeeperResponse &){});
             }
         }
@@ -157,16 +154,16 @@
         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 {
+    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) {
         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 GK_ERROR;
+            return PERMISSION_DENIED;
         }
 
         // Make sure to clear any state from before factory reset as soon as a credential is
@@ -174,189 +171,226 @@
         clear_state_if_needed();
 
         // need a desired password to enroll
-        if (desiredPassword.size() == 0) return GK_ERROR;
+        if (desired_password_length == 0) return -EINVAL;
 
-        if (!hw_device) {
-            LOG(ERROR) << "has no HAL to talk to";
-            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);
 
-        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;
+            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;
             }
-            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> newPwd;
-        newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.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);
 
-        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();
+            uint32_t hw_uid = adjust_uid(uid);
+            Return<void> hwRes = hw_device->enroll(hw_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);
                     }
-                });
-        if (!hwRes.isOk()) {
-            LOG(ERROR) << "enroll transaction failed";
-            return GK_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;
+            }
+        } 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 (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;
-            }
+        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);
+        }
 
-            const gatekeeper::password_handle_t* handle =
-                    reinterpret_cast<const gatekeeper::password_handle_t*>(
-                            gkResponse->payload().data());
+        if (ret == GATEKEEPER_RESPONSE_OK) {
+            gatekeeper::password_handle_t *handle =
+                    reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
             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.
-            auto status = verify(uid, gkResponse->payload(), desiredPassword, &verifyResponse);
-            if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
-                LOG(ERROR) << "Failed to verify password after enrolling";
-            }
+            verify(uid, *enrolled_password_handle, sizeof(password_handle_t), desired_password,
+                    desired_password_length, &rr);
         }
 
-        return Status::ok();
+        return ret;
     }
 
-    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 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 = nullptr;
+        uint32_t auth_token_length;
+        int ret = verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
+                provided_password, provided_password_length,
+                &auth_token, &auth_token_length, request_reenroll);
+        delete [] auth_token;
+        return ret;
     }
 
-    Status verifyChallenge(int32_t uid, int64_t challenge,
-                           const std::vector<uint8_t>& enrolledPasswordHandle,
-                           const std::vector<uint8_t>& providedPassword,
-                           GKResponse* gkResponse) override {
+    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) {
         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 GK_ERROR;
+            return PERMISSION_DENIED;
         }
 
         // can't verify if we're missing either param
-        if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
+        if (enrolled_password_handle == nullptr || provided_password == nullptr ||
+            enrolled_password_handle_length == 0 || provided_password_length == 0)
+            return -EINVAL;
 
-        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();
+        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) {
+                uint32_t hw_uid = adjust_uid(uid);
+                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(hw_uid, challenge, curPwdHandle, enteredPwd,
+                                        [&ret, request_reenroll, auth_token, auth_token_length]
+                                             (const GatekeeperResponse &rsp) {
+                    ret = static_cast<int>(rsp.code); // propagate errors
+                    if (auth_token != nullptr && auth_token_length != nullptr &&
+                        rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+                        *auth_token = new uint8_t[rsp.data.size()];
+                        *auth_token_length = rsp.data.size();
+                        memcpy(*auth_token, rsp.data.data(), *auth_token_length);
+                        if (request_reenroll != nullptr) {
+                            *request_reenroll = (rsp.code == GatekeeperStatusCode::STATUS_REENROLL);
+                        }
+                        ret = 0; // all success states are reported as 0
+                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
+                               rsp.timeout > 0) {
+                        ret = rsp.timeout;
                     }
                 });
+                if (!hwRes.isOk()) {
+                    ALOGE("verify transaction failed\n");
+                    ret = -1;
+                }
+            } else {
+                // upgrade scenario, a HAL has been added to this device where there was none before
+                SoftGateKeeperDevice soft_dev;
+                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 (!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.";
+                if (ret == 0) {
+                    // success! re-enroll with HAL
+                    if (request_reenroll != nullptr) *request_reenroll = true;
                 }
             }
-
-            maybe_store_sid(uid, handle->user_id);
+        } 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);
         }
 
-        return Status::ok();
+        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::keystore::IKeystoreService> service =
+                    interface_cast<security::keystore::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;
     }
 
-    Status getSecureUserId(int32_t uid, int64_t* sid) override {
-        *sid = read_sid(uid);
-        return Status::ok();
-    }
+    virtual uint64_t getSecureUserId(uint32_t uid) { return read_sid(uid); }
 
-    Status clearSecureUserId(int32_t uid) override {
+    virtual void clearSecureUserId(uint32_t uid) {
         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 Status::ok();
+            return;
         }
         clear_sid(uid);
 
-        if (hw_device) {
+        if (hw_device != nullptr) {
             uint32_t hw_uid = adjust_uid(uid);
             hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
         }
-        return Status::ok();
     }
 
-    Status reportDeviceSetupComplete() override {
+    virtual void reportDeviceSetupComplete() {
         IPCThreadState* ipc = IPCThreadState::self();
         const int calling_pid = ipc->getCallingPid();
         const int calling_uid = ipc->getCallingUid();
         if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
             ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
-            return Status::ok();
+            return;
         }
 
         clear_state_if_needed();
-        return Status::ok();
     }
 
-    status_t dump(int fd, const Vector<String16>&) override {
+    virtual status_t dump(int fd, const Vector<String16> &) {
         IPCThreadState* ipc = IPCThreadState::self();
         const int pid = ipc->getCallingPid();
         const int uid = ipc->getCallingUid();
@@ -377,6 +411,7 @@
 
 private:
     sp<IGatekeeper> hw_device;
+    std::unique_ptr<SoftGateKeeperDevice> soft_device;
 
     bool clear_state_if_needed_done;
     bool is_running_gsi;
diff --git a/gatekeeperd/include/gatekeeper/GateKeeperResponse.h b/gatekeeperd/include/gatekeeper/GateKeeperResponse.h
deleted file mode 100644
index 99fff02..0000000
--- a/gatekeeperd/include/gatekeeper/GateKeeperResponse.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-**
-** 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.bp b/gatekeeperd/tests/Android.bp
new file mode 100644
index 0000000..d4cf93b
--- /dev/null
+++ b/gatekeeperd/tests/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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_test {
+    name: "gatekeeperd-unit-tests",
+
+    cflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+        "-Wno-missing-field-initializers",
+    ],
+    shared_libs: [
+        "libgatekeeper",
+        "libcrypto",
+        "libbase",
+    ],
+    static_libs: ["libscrypt_static"],
+    include_dirs: ["external/scrypt/lib/crypto"],
+    srcs: ["gatekeeper_test.cpp"],
+}
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
new file mode 100644
index 0000000..100375f
--- /dev/null
+++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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 14d46b3..2cf6be9 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -17,10 +17,6 @@
     shared_libs: [
         "libutils",
         "libbase",
-
-        // Need latest HealthInfo definition from headers of this shared
-        // library. Clients don't need to link to this.
-        "android.hardware.health@2.1",
     ],
     header_libs: ["libhealthd_headers"],
     export_header_lib_headers: ["libhealthd_headers"],
@@ -46,6 +42,8 @@
         "libbase",
         "libcutils",
         "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
         "liblog",
         "libutils",
         "android.hardware.health@2.0",
@@ -89,7 +87,6 @@
 
 cc_library_static {
     name: "libhealthd_charger_nops",
-    recovery_available: true,
 
     srcs: [
         "healthd_mode_charger_nops.cpp",
@@ -105,138 +102,11 @@
     ],
 
     static_libs: [
-        "libhealthloop",
-        "libhealth2impl",
+        "android.hardware.health@2.0-impl",
     ],
 
     shared_libs: [
-        "android.hardware.health@2.1",
-        "libutils",
-    ],
-}
-
-sysprop_library {
-    name: "charger_sysprop",
-    recovery_available: true,
-    srcs: ["charger.sysprop"],
-    property_owner: "Platform",
-    api_packages: ["android.sysprop"],
-}
-
-cc_library_static {
-    name: "libhealthd_draw",
-    export_include_dirs: ["."],
-    static_libs: [
-        "libcharger_sysprop",
-        "libminui",
-    ],
-    shared_libs: [
-        "libbase",
-    ],
-    header_libs: ["libbatteryservice_headers"],
-
-    srcs: ["healthd_draw.cpp"],
-}
-
-cc_library_static {
-    name: "libhealthd_charger",
-    local_include_dirs: ["include"],
-    export_include_dirs: [".", "include"],
-
-    static_libs: [
-        "android.hardware.health@1.0-convert",
-        "libcharger_sysprop",
-        "libhealthd_draw",
-        "libhealthloop",
-        "libhealth2impl",
-        "libminui",
-    ],
-
-    shared_libs: [
-        "android.hardware.health@2.1",
-        "libbase",
-        "libcutils",
-        "liblog",
-        "libpng",
-        "libsuspend",
-        "libutils",
-    ],
-
-    srcs: [
-        "healthd_mode_charger.cpp",
-        "AnimationParser.cpp",
-    ],
-}
-
-cc_defaults {
-    name: "charger_defaults",
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    shared_libs: [
-        // common
         "android.hardware.health@2.0",
-        "android.hardware.health@2.1",
-        "libbase",
-        "libcutils",
-        "libhidlbase",
-        "liblog",
         "libutils",
-
-        // system charger only
-        "libpng",
     ],
-
-    static_libs: [
-        // common
-        "android.hardware.health@1.0-convert",
-        "libbatterymonitor",
-        "libcharger_sysprop",
-        "libhealthd_charger_nops",
-        "libhealthloop",
-        "libhealth2impl",
-
-        // system charger only
-        "libhealthd_draw",
-        "libhealthd_charger",
-        "libminui",
-        "libsuspend",
-    ],
-}
-
-cc_binary {
-    name: "charger",
-    defaults: ["charger_defaults"],
-    recovery_available: true,
-    srcs: [
-        "charger.cpp",
-        "charger_utils.cpp",
-    ],
-
-    target: {
-        recovery: {
-            // No UI and libsuspend for recovery charger.
-            cflags: [
-                "-DCHARGER_FORCE_NO_UI=1",
-            ],
-            exclude_shared_libs: [
-                "libpng",
-            ],
-            exclude_static_libs: [
-                "libhealthd_draw",
-                "libhealthd_charger",
-                "libminui",
-                "libsuspend",
-            ],
-        }
-    }
-}
-
-cc_test {
-    name: "charger_test",
-    defaults: ["charger_defaults"],
-    srcs: ["charger_test.cpp"],
 }
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 4b09cf8..05123af 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -2,10 +2,189 @@
 
 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
+LOCAL_SHARED_LIBRARIES := libbase
+LOCAL_SRC_FILES := healthd_draw.cpp
+
+ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_SCREEN),)
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=$(TARGET_HEALTHD_DRAW_SPLIT_SCREEN)
+else
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=0
+endif
+
+ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_OFFSET),)
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=$(TARGET_HEALTHD_DRAW_SPLIT_OFFSET)
+else
+LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
+endif
+
+LOCAL_HEADER_LIBRARIES := libbatteryservice_headers
+
+include $(BUILD_STATIC_LIBRARY)
+
+### libhealthd_charger ###
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -Werror
+ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
+LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
+endif
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+endif
+
+LOCAL_SRC_FILES := \
+    healthd_mode_charger.cpp \
+    AnimationParser.cpp
+
+LOCAL_MODULE := libhealthd_charger
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+    $(LOCAL_PATH) \
+    $(LOCAL_PATH)/include
+
+LOCAL_STATIC_LIBRARIES := \
+    android.hardware.health@2.0-impl \
+    android.hardware.health@1.0-convert \
+    libhealthstoragedefault \
+    libhealthd_draw \
+    libminui \
+
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.health@2.0 \
+    libbase \
+    libcutils \
+    liblog \
+    libpng \
+    libutils \
+
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_SHARED_LIBRARIES += libsuspend
+endif
+
+include $(BUILD_STATIC_LIBRARY)
+
+### charger ###
+include $(CLEAR_VARS)
 ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
 LOCAL_CHARGER_NO_UI := true
 endif
 
+LOCAL_SRC_FILES := \
+    charger.cpp \
+
+LOCAL_MODULE := charger
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+LOCAL_CFLAGS := -Werror
+ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+LOCAL_CFLAGS += -DCHARGER_NO_UI
+endif
+
+CHARGER_STATIC_LIBRARIES := \
+    android.hardware.health@2.0-impl \
+    android.hardware.health@1.0-convert \
+    libbinderthreadstate \
+    libhidltransport \
+    libhidlbase \
+    libhealthstoragedefault \
+    libvndksupport \
+    libhealthd_charger \
+    libhealthd_charger_nops \
+    libhealthd_draw \
+    libbatterymonitor \
+
+CHARGER_SHARED_LIBRARIES := \
+    android.hardware.health@2.0 \
+    libbase \
+    libcutils \
+    libjsoncpp \
+    libprocessgroup \
+    liblog \
+    libutils \
+
+ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
+CHARGER_STATIC_LIBRARIES += libminui
+CHARGER_SHARED_LIBRARIES += libpng
+endif
+
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+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 /system/bin/charger
+LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
+    && ln -sf /system/bin/charger $(TARGET_ROOT_OUT)/charger
+
+include $(BUILD_EXECUTABLE)
+
+### charger.recovery ###
+include $(CLEAR_VARS)
+
+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 \
+    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
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
index fde3b95..864038b 100644
--- a/healthd/AnimationParser.cpp
+++ b/healthd/AnimationParser.cpp
@@ -84,6 +84,7 @@
     static constexpr const char* fail_prefix = "fail: ";
     static constexpr const char* clock_prefix = "clock_display: ";
     static constexpr const char* percent_prefix = "percent_display: ";
+    static constexpr const char* frame_prefix = "frame: ";
 
     std::vector<animation::frame> frames;
 
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index bdf4aac..06c8176 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -29,12 +29,10 @@
 
 #include <algorithm>
 #include <memory>
-#include <optional>
 
 #include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
-#include <android/hardware/health/2.1/types.h>
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 #include <cutils/properties.h>
@@ -49,106 +47,97 @@
 #define MILLION 1.0e6
 #define DEFAULT_VBUS_VOLTAGE 5000000
 
-using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
-using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
-using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
-using android::hardware::health::V1_0::BatteryHealth;
-using android::hardware::health::V1_0::BatteryStatus;
-using android::hardware::health::V2_1::BatteryCapacityLevel;
-
 namespace android {
 
-template <typename T>
-struct SysfsStringEnumMap {
+struct sysfsStringEnumMap {
     const char* s;
-    T val;
+    int val;
 };
 
-template <typename T>
-static std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {
+static int mapSysfsString(const char* str,
+                          struct sysfsStringEnumMap map[]) {
     for (int i = 0; map[i].s; i++)
         if (!strcmp(str, map[i].s))
             return map[i].val;
 
-    return std::nullopt;
+    return -1;
 }
 
-static void initHealthInfo(HealthInfo_2_1* health_info_2_1) {
-    *health_info_2_1 = HealthInfo_2_1{};
-
-    // HIDL enum values are zero initialized, so they need to be initialized
-    // properly.
-    health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
-    auto* props = &health_info_2_1->legacy.legacy;
-    props->batteryStatus = BatteryStatus::UNKNOWN;
-    props->batteryHealth = BatteryHealth::UNKNOWN;
+static void initBatteryProperties(BatteryProperties* props) {
+    props->chargerAcOnline = false;
+    props->chargerUsbOnline = false;
+    props->chargerWirelessOnline = false;
+    props->maxChargingCurrent = 0;
+    props->maxChargingVoltage = 0;
+    props->batteryStatus = BATTERY_STATUS_UNKNOWN;
+    props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
+    props->batteryPresent = false;
+    props->batteryLevel = 0;
+    props->batteryVoltage = 0;
+    props->batteryTemperature = 0;
+    props->batteryCurrent = 0;
+    props->batteryCycleCount = 0;
+    props->batteryFullCharge = 0;
+    props->batteryChargeCounter = 0;
+    props->batteryTechnology.clear();
 }
 
 BatteryMonitor::BatteryMonitor()
     : mHealthdConfig(nullptr),
       mBatteryDevicePresent(false),
       mBatteryFixedCapacity(0),
-      mBatteryFixedTemperature(0),
-      mHealthInfo(std::make_unique<HealthInfo_2_1>()) {
-    initHealthInfo(mHealthInfo.get());
+      mBatteryFixedTemperature(0) {
+    initBatteryProperties(&props);
 }
 
-BatteryMonitor::~BatteryMonitor() {}
-
-const HealthInfo_1_0& BatteryMonitor::getHealthInfo_1_0() const {
-    return getHealthInfo_2_0().legacy;
+struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
+    return batteryMonitor->props;
 }
 
-const HealthInfo_2_0& BatteryMonitor::getHealthInfo_2_0() const {
-    return getHealthInfo_2_1().legacy;
-}
-
-const HealthInfo_2_1& BatteryMonitor::getHealthInfo_2_1() const {
-    return *mHealthInfo;
-}
-
-BatteryStatus getBatteryStatus(const char* status) {
-    static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {
-            {"Unknown", BatteryStatus::UNKNOWN},
-            {"Charging", BatteryStatus::CHARGING},
-            {"Discharging", BatteryStatus::DISCHARGING},
-            {"Not charging", BatteryStatus::NOT_CHARGING},
-            {"Full", BatteryStatus::FULL},
-            {NULL, BatteryStatus::UNKNOWN},
+int BatteryMonitor::getBatteryStatus(const char* status) {
+    int ret;
+    struct sysfsStringEnumMap batteryStatusMap[] = {
+        { "Unknown", BATTERY_STATUS_UNKNOWN },
+        { "Charging", BATTERY_STATUS_CHARGING },
+        { "Discharging", BATTERY_STATUS_DISCHARGING },
+        { "Not charging", BATTERY_STATUS_NOT_CHARGING },
+        { "Full", BATTERY_STATUS_FULL },
+        { NULL, 0 },
     };
 
-    auto ret = mapSysfsString(status, batteryStatusMap);
-    if (!ret) {
+    ret = mapSysfsString(status, batteryStatusMap);
+    if (ret < 0) {
         KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
-        *ret = BatteryStatus::UNKNOWN;
+        ret = BATTERY_STATUS_UNKNOWN;
     }
 
-    return *ret;
+    return ret;
 }
 
-BatteryHealth getBatteryHealth(const char* status) {
-    static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
-            {"Unknown", BatteryHealth::UNKNOWN},
-            {"Good", BatteryHealth::GOOD},
-            {"Overheat", BatteryHealth::OVERHEAT},
-            {"Dead", BatteryHealth::DEAD},
-            {"Over voltage", BatteryHealth::OVER_VOLTAGE},
-            {"Unspecified failure", BatteryHealth::UNSPECIFIED_FAILURE},
-            {"Cold", BatteryHealth::COLD},
-            // battery health values from JEITA spec
-            {"Warm", BatteryHealth::GOOD},
-            {"Cool", BatteryHealth::GOOD},
-            {"Hot", BatteryHealth::OVERHEAT},
-            {NULL, BatteryHealth::UNKNOWN},
+int BatteryMonitor::getBatteryHealth(const char* status) {
+    int ret;
+    struct sysfsStringEnumMap batteryHealthMap[] = {
+        { "Unknown", BATTERY_HEALTH_UNKNOWN },
+        { "Good", BATTERY_HEALTH_GOOD },
+        { "Overheat", BATTERY_HEALTH_OVERHEAT },
+        { "Dead", BATTERY_HEALTH_DEAD },
+        { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },
+        { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },
+        { "Cold", BATTERY_HEALTH_COLD },
+        // battery health values from JEITA spec
+        { "Warm", BATTERY_HEALTH_GOOD },
+        { "Cool", BATTERY_HEALTH_GOOD },
+        { "Hot", BATTERY_HEALTH_OVERHEAT },
+        { NULL, 0 },
     };
 
-    auto ret = mapSysfsString(status, batteryHealthMap);
-    if (!ret) {
+    ret = mapSysfsString(status, batteryHealthMap);
+    if (ret < 0) {
         KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
-        *ret = BatteryHealth::UNKNOWN;
+        ret = BATTERY_HEALTH_UNKNOWN;
     }
 
-    return *ret;
+    return ret;
 }
 
 int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
@@ -159,34 +148,35 @@
 }
 
 BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
-    static SysfsStringEnumMap<int> supplyTypeMap[] = {
-            {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
-            {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},
-            {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},
-            {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
-            {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
-            {NULL, 0},
-    };
     std::string buf;
+    int ret;
+    struct sysfsStringEnumMap supplyTypeMap[] = {
+            { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
+            { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
+            { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB", ANDROID_POWER_SUPPLY_TYPE_USB },
+            { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
+            { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
+            { NULL, 0 },
+    };
 
     if (readFromFile(path, &buf) <= 0)
         return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
 
-    auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
-    if (!ret) {
+    ret = mapSysfsString(buf.c_str(), supplyTypeMap);
+    if (ret < 0) {
         KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
-        *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+        ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
     }
 
-    return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
+    return static_cast<BatteryMonitor::PowerSupplyType>(ret);
 }
 
 bool BatteryMonitor::getBooleanField(const String8& path) {
@@ -210,10 +200,10 @@
     return value;
 }
 
-void BatteryMonitor::updateValues(void) {
-    initHealthInfo(mHealthInfo.get());
+bool BatteryMonitor::update(void) {
+    bool logthis;
 
-    HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
+    initBatteryProperties(&props);
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -237,15 +227,6 @@
     if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
         props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
 
-    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
-        mHealthInfo->legacy.batteryCurrentAverage =
-                getIntField(mHealthdConfig->batteryCurrentAvgPath);
-
-    // TODO(b/142260281): Retrieve these values correctly.
-    mHealthInfo->batteryCapacityLevel = BatteryCapacityLevel::UNKNOWN;
-    mHealthInfo->batteryChargeTimeToFullNowSeconds = 0;
-    mHealthInfo->batteryFullCapacityUah = props.batteryFullCharge;
-
     props.batteryTemperature = mBatteryFixedTemperature ?
         mBatteryFixedTemperature :
         getIntField(mHealthdConfig->batteryTemperaturePath);
@@ -308,58 +289,62 @@
             }
         }
     }
-}
 
-void BatteryMonitor::logValues(void) {
-    char dmesgline[256];
-    size_t len;
-    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
-    if (props.batteryPresent) {
-        snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
-                 props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
-                 abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
-                 props.batteryHealth, props.batteryStatus);
+    logthis = !healthd_board_battery_update(&props);
 
-        len = strlen(dmesgline);
-        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
-            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
-                            props.batteryCurrent);
+    if (logthis) {
+        char dmesgline[256];
+        size_t len;
+        if (props.batteryPresent) {
+            snprintf(dmesgline, sizeof(dmesgline),
+                 "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
+                 props.batteryLevel, props.batteryVoltage,
+                 props.batteryTemperature < 0 ? "-" : "",
+                 abs(props.batteryTemperature / 10),
+                 abs(props.batteryTemperature % 10), props.batteryHealth,
+                 props.batteryStatus);
+
+            len = strlen(dmesgline);
+            if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " c=%d", props.batteryCurrent);
+            }
+
+            if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " fc=%d", props.batteryFullCharge);
+            }
+
+            if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " cc=%d", props.batteryCycleCount);
+            }
+        } else {
+            len = snprintf(dmesgline, sizeof(dmesgline),
+                 "battery none");
         }
 
-        if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
-            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
-                            props.batteryFullCharge);
-        }
+        snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
+                 props.chargerAcOnline ? "a" : "",
+                 props.chargerUsbOnline ? "u" : "",
+                 props.chargerWirelessOnline ? "w" : "");
 
-        if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
-            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
-                            props.batteryCycleCount);
-        }
-    } else {
-        len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
+        KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
     }
 
-    snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
-             props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
-             props.chargerWirelessOnline ? "w" : "");
-
-    KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
-}
-
-bool BatteryMonitor::isChargerOnline() {
-    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
+    healthd_mode_ops->battery_update(&props);
     return props.chargerAcOnline | props.chargerUsbOnline |
             props.chargerWirelessOnline;
 }
 
 int BatteryMonitor::getChargeStatus() {
-    BatteryStatus result = BatteryStatus::UNKNOWN;
+    int result = BATTERY_STATUS_UNKNOWN;
     if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
         std::string buf;
         if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
             result = getBatteryStatus(buf.c_str());
     }
-    return static_cast<int>(result);
+    return result;
 }
 
 status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
@@ -432,7 +417,6 @@
 void BatteryMonitor::dumpState(int fd) {
     int v;
     char vs[128];
-    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
 
     snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
              props.chargerAcOnline, props.chargerUsbOnline,
diff --git a/healthd/animation.h b/healthd/animation.h
index d02d7a7..9476c91 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -75,7 +75,7 @@
 
     bool run;
 
-    frame* frames = nullptr;
+    frame* frames;
     int cur_frame;
     int num_frames;
     int first_frame_repeats;  // Number of times to repeat the first frame in the current cycle
@@ -85,8 +85,6 @@
 
     int cur_level;  // current battery level being animated (0-100)
     int cur_status;  // current battery status - see BatteryService.h for BATTERY_STATUS_*
-
-    ~animation() { delete frames; }
 };
 
 }
diff --git a/healthd/api/charger_sysprop-current.txt b/healthd/api/charger_sysprop-current.txt
deleted file mode 100644
index 678c847..0000000
--- a/healthd/api/charger_sysprop-current.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-props {
-  module: "android.sysprop.ChargerProperties"
-  prop {
-    api_name: "disable_init_blank"
-    scope: Internal
-    prop_name: "ro.charger.disable_init_blank"
-  }
-  prop {
-    api_name: "draw_split_offset"
-    type: Long
-    scope: Internal
-    prop_name: "ro.charger.draw_split_offset"
-  }
-  prop {
-    api_name: "draw_split_screen"
-    scope: Internal
-    prop_name: "ro.charger.draw_split_screen"
-  }
-  prop {
-    api_name: "enable_suspend"
-    scope: Internal
-    prop_name: "ro.charger.enable_suspend"
-  }
-  prop {
-    api_name: "no_ui"
-    scope: Internal
-    prop_name: "ro.charger.no_ui"
-  }
-}
diff --git a/healthd/api/charger_sysprop-latest.txt b/healthd/api/charger_sysprop-latest.txt
deleted file mode 100644
index 678c847..0000000
--- a/healthd/api/charger_sysprop-latest.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-props {
-  module: "android.sysprop.ChargerProperties"
-  prop {
-    api_name: "disable_init_blank"
-    scope: Internal
-    prop_name: "ro.charger.disable_init_blank"
-  }
-  prop {
-    api_name: "draw_split_offset"
-    type: Long
-    scope: Internal
-    prop_name: "ro.charger.draw_split_offset"
-  }
-  prop {
-    api_name: "draw_split_screen"
-    scope: Internal
-    prop_name: "ro.charger.draw_split_screen"
-  }
-  prop {
-    api_name: "enable_suspend"
-    scope: Internal
-    prop_name: "ro.charger.enable_suspend"
-  }
-  prop {
-    api_name: "no_ui"
-    scope: Internal
-    prop_name: "ro.charger.no_ui"
-  }
-}
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 58ed416..085cceb 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -14,18 +14,13 @@
  * limitations under the License.
  */
 
-#include "charger.sysprop.h"
 #include "healthd_mode_charger.h"
 #include "healthd_mode_charger_nops.h"
 
-#ifndef CHARGER_FORCE_NO_UI
-#define CHARGER_FORCE_NO_UI 0
-#endif
-
 int main(int argc, char** argv) {
-    if (CHARGER_FORCE_NO_UI || android::sysprop::ChargerProperties::no_ui().value_or(false)) {
-        return healthd_charger_nops(argc, argv);
-    } else {
-        return healthd_charger_main(argc, argv);
-    }
+#ifdef CHARGER_NO_UI
+    return healthd_charger_nops(argc, argv);
+#else
+    return healthd_charger_main(argc, argv);
+#endif
 }
diff --git a/healthd/charger.sysprop b/healthd/charger.sysprop
deleted file mode 100644
index b3f47a1..0000000
--- a/healthd/charger.sysprop
+++ /dev/null
@@ -1,38 +0,0 @@
-owner: Platform
-module: "android.sysprop.ChargerProperties"
-
-prop {
-    api_name: "draw_split_screen"
-    type: Boolean
-    prop_name: "ro.charger.draw_split_screen"
-    scope: Internal
-    access: Readonly
-}
-prop {
-    api_name: "draw_split_offset"
-    type: Long
-    prop_name: "ro.charger.draw_split_offset"
-    scope: Internal
-    access: Readonly
-}
-prop {
-    api_name: "disable_init_blank"
-    type: Boolean
-    prop_name: "ro.charger.disable_init_blank"
-    scope: Internal
-    access: Readonly
-}
-prop {
-    api_name: "enable_suspend"
-    type: Boolean
-    prop_name: "ro.charger.enable_suspend"
-    scope: Internal
-    access: Readonly
-}
-prop {
-    api_name: "no_ui"
-    type: Boolean
-    prop_name: "ro.charger.no_ui"
-    scope: Internal
-    access: Readonly
-}
diff --git a/healthd/charger_test.cpp b/healthd/charger_test.cpp
index e0bde68..a7e2161 100644
--- a/healthd/charger_test.cpp
+++ b/healthd/charger_test.cpp
@@ -21,22 +21,13 @@
 #include <condition_variable>
 #include <fstream>
 #include <iostream>
-#include <memory>
 #include <mutex>
 #include <streambuf>
 #include <string>
 #include <thread>
 #include <vector>
 
-#include <health/utils.h>
-#include <health2impl/Health.h>
-
-#include "healthd_mode_charger.h"
-
-using android::hardware::health::InitHealthdConfig;
-using android::hardware::health::V2_1::HealthInfo;
-using android::hardware::health::V2_1::IHealth;
-using android::hardware::health::V2_1::implementation::Health;
+#include <health2/Health.h>
 
 #define LOG_THIS(fmt, ...)     \
     ALOGE(fmt, ##__VA_ARGS__); \
@@ -138,23 +129,22 @@
     config->screen_on = NULL;
 }
 
-class TestHealth : public Health {
-  protected:
-    using Health::Health;
-    void UpdateHealthInfo(HealthInfo*) override { getUpdateNotifier().set(true /* updated */); }
-};
+int healthd_board_battery_update(struct android::BatteryProperties*) {
+    getUpdateNotifier().set(true /* updated */);
 
-int main(int /*argc*/, char** /*argv*/) {
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
+
+extern int healthd_charger_main(int argc, char** argv);
+
+int main(int argc, char** argv) {
+    using android::hardware::health::V2_0::implementation::Health;
+
     const char* dumpFile = "/data/local/tmp/dump.txt";
 
-    auto config = std::make_unique<healthd_config>();
-    InitHealthdConfig(config.get());
-    healthd_board_init(config.get());
-    sp<IHealth> passthrough = new TestHealth(std::move(config));
-
     std::thread bgThread([=] {
-        android::Charger charger(passthrough);
-        charger.StartLoop();
+        healthd_charger_main(argc, argv);
     });
 
     // wait for healthd_init to finish
@@ -163,7 +153,7 @@
         exit(1);
     }
 
-    passthrough->debug(createHidlHandle(dumpFile), {} /* options */);
+    Health::getImplementation()->debug(createHidlHandle(dumpFile), {} /* options */);
 
     std::string content = openToString(dumpFile);
     int status = expectContains(content, {
diff --git a/healthd/charger_utils.cpp b/healthd/charger_utils.cpp
deleted file mode 100644
index 0cf9df5..0000000
--- a/healthd/charger_utils.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 "charger_utils.h"
-
-#include <android-base/logging.h>
-#include <android/hidl/manager/1.0/IServiceManager.h>
-#include <health/utils.h>
-#include <health2impl/Health.h>
-#include <hidl/ServiceManagement.h>
-
-using android::hardware::getPassthroughServiceManager;
-using android::hidl::base::V1_0::IBase;
-using android::hidl::manager::V1_0::IServiceManager;
-
-namespace android {
-namespace hardware {
-namespace health {
-sp<V2_1::IHealth> GetPassthroughHealthImpl() {
-    // Not using getService() because there is no hwservicemanager in charger mode.
-    sp<IServiceManager> pm = getPassthroughServiceManager();
-    if (pm == nullptr) {
-        LOG(WARNING) << "Cannot get passthrough service manager.";
-        return nullptr;
-    }
-    sp<IBase> base = pm->get(V2_0::IHealth::descriptor, "default");
-    if (base == nullptr) {
-        LOG(WARNING) << "Cannot find passthrough implementation of health 2.0 HAL for instance "
-                        "'default' on the device.";
-        return nullptr;
-    }
-    sp<V2_1::IHealth> service = V2_1::IHealth::castFrom(base);
-    if (service == nullptr) {
-        LOG(WARNING)
-                << "Cannot cast passthrough implementation of health 2.0 HAL to 2.1 for instance "
-                   "'default' on the device.";
-        return nullptr;
-    }
-    return service;
-}
-
-sp<V2_1::IHealth> GetPassthroughHealth() {
-    auto impl = GetPassthroughHealthImpl();
-    if (impl == nullptr) {
-        LOG(WARNING) << "Charger uses system defaults.";
-        auto config = std::make_unique<healthd_config>();
-        InitHealthdConfig(config.get());
-        impl = new V2_1::implementation::Health(std::move(config));
-    }
-    return impl;
-}
-
-}  // namespace health
-}  // namespace hardware
-}  // namespace android
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 50eee19..3da8bda 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -18,34 +18,15 @@
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 
-#include "charger.sysprop.h"
 #include "healthd_draw.h"
 
 #define LOGE(x...) KLOG_ERROR("charger", x);
 #define LOGW(x...) KLOG_WARNING("charger", x);
 #define LOGV(x...) KLOG_DEBUG("charger", x);
 
-static bool get_split_screen() {
-    return android::sysprop::ChargerProperties::draw_split_screen().value_or(false);
-}
-
-static int get_split_offset() {
-    int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0);
-    if (value < static_cast<int64_t>(std::numeric_limits<int>::min())) {
-        LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
-             std::numeric_limits<int>::min());
-        value = std::numeric_limits<int>::min();
-    }
-    if (value > static_cast<int64_t>(std::numeric_limits<int>::max())) {
-        LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
-             std::numeric_limits<int>::max());
-        value = std::numeric_limits<int>::max();
-    }
-    return static_cast<int>(value);
-}
-
 HealthdDraw::HealthdDraw(animation* anim)
-    : kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
+  : kSplitScreen(HEALTHD_DRAW_SPLIT_SCREEN),
+    kSplitOffset(HEALTHD_DRAW_SPLIT_OFFSET) {
     int ret = gr_init();
 
     if (ret < 0) {
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 7d844c9..0e5aa4f 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include "healthd_mode_charger.h"
-
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -30,7 +28,7 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <optional>
+#include <functional>
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
@@ -45,34 +43,21 @@
 #include <cutils/uevent.h>
 #include <sys/reboot.h>
 
+#ifdef CHARGER_ENABLE_SUSPEND
 #include <suspend/autosuspend.h>
+#endif
 
 #include "AnimationParser.h"
-#include "charger.sysprop.h"
-#include "charger_utils.h"
 #include "healthd_draw.h"
 
-#include <android/hardware/health/2.0/IHealthInfoCallback.h>
-#include <health/utils.h>
-#include <health2impl/HalHealthLoop.h>
-#include <health2impl/Health.h>
+#include <health2/Health.h>
 #include <healthd/healthd.h>
 
 using namespace android;
-using android::hardware::Return;
-using android::hardware::health::GetPassthroughHealth;
-using android::hardware::health::HealthLoop;
-using android::hardware::health::V1_0::BatteryStatus;
-using android::hardware::health::V2_0::Result;
-using android::hardware::health::V2_1::IHealth;
-using IHealth_2_0 = android::hardware::health::V2_0::IHealth;
-using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
-using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
 
 // main healthd loop
 extern int healthd_main(void);
 
-// minui globals
 char* locale;
 
 #ifndef max
@@ -93,7 +78,6 @@
 #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)
 
@@ -101,8 +85,6 @@
 #define LOGW(x...) KLOG_WARNING("charger", x);
 #define LOGV(x...) KLOG_DEBUG("charger", x);
 
-namespace android {
-
 // 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.
@@ -111,6 +93,28 @@
 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;
+    bool down;
+    int64_t timestamp;
+};
+
+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];
+
+    animation* batt_anim;
+    GRSurface* surf_unknown;
+    int boot_min_cap;
+};
+
 static const animation BASE_ANIMATION = {
     .text_clock =
         {
@@ -149,51 +153,51 @@
     .cur_status = BATTERY_STATUS_UNKNOWN,
 };
 
-void Charger::InitDefaultAnimationFrames() {
-    owned_frames_ = {
-            {
-                    .disp_time = 750,
-                    .min_level = 0,
-                    .max_level = 19,
-                    .surface = NULL,
-            },
-            {
-                    .disp_time = 750,
-                    .min_level = 0,
-                    .max_level = 39,
-                    .surface = NULL,
-            },
-            {
-                    .disp_time = 750,
-                    .min_level = 0,
-                    .max_level = 59,
-                    .surface = NULL,
-            },
-            {
-                    .disp_time = 750,
-                    .min_level = 0,
-                    .max_level = 79,
-                    .surface = NULL,
-            },
-            {
-                    .disp_time = 750,
-                    .min_level = 80,
-                    .max_level = 95,
-                    .surface = NULL,
-            },
-            {
-                    .disp_time = 750,
-                    .min_level = 0,
-                    .max_level = 100,
-                    .surface = NULL,
-            },
-    };
-}
+static animation::frame default_animation_frames[] = {
+    {
+        .disp_time = 750,
+        .min_level = 0,
+        .max_level = 19,
+        .surface = NULL,
+    },
+    {
+        .disp_time = 750,
+        .min_level = 0,
+        .max_level = 39,
+        .surface = NULL,
+    },
+    {
+        .disp_time = 750,
+        .min_level = 0,
+        .max_level = 59,
+        .surface = NULL,
+    },
+    {
+        .disp_time = 750,
+        .min_level = 0,
+        .max_level = 79,
+        .surface = NULL,
+    },
+    {
+        .disp_time = 750,
+        .min_level = 80,
+        .max_level = 95,
+        .surface = NULL,
+    },
+    {
+        .disp_time = 750,
+        .min_level = 0,
+        .max_level = 100,
+        .surface = NULL,
+    },
+};
 
-Charger::Charger(const sp<IHealth>& service)
-    : HalHealthLoop("charger", service), batt_anim_(BASE_ANIMATION) {}
+static animation battery_animation = BASE_ANIMATION;
 
-Charger::~Charger() {}
+static charger charger_state;
+static healthd_config* healthd_config;
+static android::BatteryProperties* batt_prop;
+static std::unique_ptr<HealthdDraw> healthd_draw;
 
 /* current time in milliseconds */
 static int64_t curr_time_ms() {
@@ -259,16 +263,18 @@
     LOGW("\n");
 }
 
+#ifdef CHARGER_ENABLE_SUSPEND
 static int request_suspend(bool enable) {
-    if (!android::sysprop::ChargerProperties::enable_suspend().value_or(false)) {
-        return 0;
-    }
-
     if (enable)
         return autosuspend_enable();
     else
         return autosuspend_disable();
 }
+#else
+static int request_suspend(bool /*enable*/) {
+    return 0;
+}
+#endif
 
 static void kick_animation(animation* anim) {
     anim->run = true;
@@ -280,125 +286,123 @@
     anim->run = false;
 }
 
-void Charger::UpdateScreenState(int64_t now) {
+static void update_screen_state(charger* charger, int64_t now) {
+    animation* batt_anim = charger->batt_anim;
     int disp_time;
 
-    if (!batt_anim_.run || now < next_screen_transition_) return;
+    if (!batt_anim->run || now < charger->next_screen_transition) return;
 
     // If battery level is not ready, keep checking in the defined time
-    if (health_info_.batteryLevel == 0 && health_info_.batteryStatus == BatteryStatus::UNKNOWN) {
-        if (wait_batt_level_timestamp_ == 0) {
+    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
-            wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME;
+            charger->wait_batt_level_timestamp = now + MAX_BATT_LEVEL_WAIT_TIME;
             LOGV("[%" PRId64 "] wait for battery capacity ready\n", now);
             return;
-        } else if (now <= wait_batt_level_timestamp_) {
+        } 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) {
-        std::optional<bool> out_screen_on;
-        service()->shouldKeepScreenOn([&](Result res, bool screen_on) {
-            if (res == Result::SUCCESS) {
-                *out_screen_on = screen_on;
-            }
-        });
-        if (out_screen_on.has_value()) {
-            if (!*out_screen_on) {
+    if (healthd_draw == nullptr) {
+        if (healthd_config && healthd_config->screen_on) {
+            if (!healthd_config->screen_on(batt_prop)) {
                 LOGV("[%" PRId64 "] leave screen off\n", now);
-                batt_anim_.run = false;
-                next_screen_transition_ = -1;
-                if (charger_online()) request_suspend(true);
+                batt_anim->run = false;
+                charger->next_screen_transition = -1;
+                if (charger->charger_connected) request_suspend(true);
                 return;
             }
         }
 
-        healthd_draw_.reset(new HealthdDraw(&batt_anim_));
+        healthd_draw.reset(new HealthdDraw(batt_anim));
 
-        if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
-            healthd_draw_->blank_screen(true);
-            screen_blanked_ = true;
-        }
+#ifndef CHARGER_DISABLE_INIT_BLANK
+        healthd_draw->blank_screen(true);
+        charger->screen_blanked = true;
+#endif
     }
 
     /* animation is over, blank screen and leave */
-    if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
-        reset_animation(&batt_anim_);
-        next_screen_transition_ = -1;
-        healthd_draw_->blank_screen(true);
-        screen_blanked_ = true;
+    if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
+        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_online()) request_suspend(true);
+        if (charger->charger_connected) request_suspend(true);
         return;
     }
 
-    disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time;
+    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
 
-    if (screen_blanked_) {
-        healthd_draw_->blank_screen(false);
-        screen_blanked_ = 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) {
+    if (batt_anim->cur_frame == 0) {
         LOGV("[%" PRId64 "] animation starting\n", now);
-        batt_anim_.cur_level = health_info_.batteryLevel;
-        batt_anim_.cur_status = (int)health_info_.batteryStatus;
-        if (health_info_.batteryLevel >= 0 && batt_anim_.num_frames != 0) {
-            /* find first frame given current battery level */
-            for (int i = 0; i < batt_anim_.num_frames; i++) {
-                if (batt_anim_.cur_level >= batt_anim_.frames[i].min_level &&
-                    batt_anim_.cur_level <= batt_anim_.frames[i].max_level) {
-                    batt_anim_.cur_frame = i;
-                    break;
+        if (batt_prop) {
+            batt_anim->cur_level = batt_prop->batteryLevel;
+            batt_anim->cur_status = batt_prop->batteryStatus;
+            if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
+                /* find first frame given current battery level */
+                for (int i = 0; i < batt_anim->num_frames; i++) {
+                    if (batt_anim->cur_level >= batt_anim->frames[i].min_level &&
+                        batt_anim->cur_level <= batt_anim->frames[i].max_level) {
+                        batt_anim->cur_frame = i;
+                        break;
+                    }
                 }
-            }
 
-            if (charger_online()) {
-                // 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;
-            }
+                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);
+                LOGV("cur_frame=%d disp_time=%d\n", batt_anim->cur_frame, disp_time);
+            }
         }
     }
 
     /* draw the new frame (@ cur_frame) */
-    healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);
+    healthd_draw->redraw_screen(charger->batt_anim, charger->surf_unknown);
 
     /* if we don't have anim frames, we only have one image, so just bump
      * the cycle counter and exit
      */
-    if (batt_anim_.num_frames == 0 || batt_anim_.cur_level < 0) {
+    if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) {
         LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now);
-        next_screen_transition_ = now + BATTERY_UNKNOWN_TIME;
-        batt_anim_.cur_cycle++;
+        charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
+        batt_anim->cur_cycle++;
         return;
     }
 
     /* schedule next screen transition */
-    next_screen_transition_ = curr_time_ms() + 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
      */
-    if (charger_online()) {
-        batt_anim_.cur_frame++;
+    if (charger->charger_connected) {
+        batt_anim->cur_frame++;
 
-        while (batt_anim_.cur_frame < batt_anim_.num_frames &&
-               (batt_anim_.cur_level < batt_anim_.frames[batt_anim_.cur_frame].min_level ||
-                batt_anim_.cur_level > batt_anim_.frames[batt_anim_.cur_frame].max_level)) {
-            batt_anim_.cur_frame++;
+        while (batt_anim->cur_frame < batt_anim->num_frames &&
+               (batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level ||
+                batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) {
+            batt_anim->cur_frame++;
         }
-        if (batt_anim_.cur_frame >= batt_anim_.num_frames) {
-            batt_anim_.cur_cycle++;
-            batt_anim_.cur_frame = 0;
+        if (batt_anim->cur_frame >= batt_anim->num_frames) {
+            batt_anim->cur_cycle++;
+            batt_anim->cur_frame = 0;
 
             /* don't reset the cycle counter, since we use that as a signal
              * in a test above to check if animation is over
@@ -409,29 +413,29 @@
          * If we stop it immediately instead of going through this loop, then
          * the animation would stop somewhere in the middle.
          */
-        batt_anim_.cur_frame = 0;
-        batt_anim_.cur_cycle++;
+        batt_anim->cur_frame = 0;
+        batt_anim->cur_cycle++;
     }
 }
 
-int Charger::SetKeyCallback(int code, int value) {
+static int set_key_callback(charger* charger, int code, int value) {
     int64_t now = curr_time_ms();
     int down = !!value;
 
     if (code > KEY_MAX) return -1;
 
     /* ignore events that don't modify our state */
-    if (keys_[code].down == down) return 0;
+    if (charger->keys[code].down == down) return 0;
 
     /* only record the down even timestamp, as the amount
      * of time the key spent not being pressed is not useful */
-    if (down) keys_[code].timestamp = now;
-    keys_[code].down = down;
-    keys_[code].pending = true;
+    if (down) charger->keys[code].timestamp = now;
+    charger->keys[code].down = down;
+    charger->keys[code].pending = true;
     if (down) {
         LOGV("[%" PRId64 "] key[%d] down\n", now, code);
     } else {
-        int64_t duration = now - keys_[code].timestamp;
+        int64_t duration = now - charger->keys[code].timestamp;
         int64_t secs = duration / 1000;
         int64_t msecs = duration - secs * 1000;
         LOGV("[%" PRId64 "] key[%d] up (was down for %" PRId64 ".%" PRId64 "sec)\n", now, code,
@@ -441,19 +445,20 @@
     return 0;
 }
 
-void Charger::UpdateInputState(input_event* ev) {
+static void update_input_state(charger* charger, input_event* ev) {
     if (ev->type != EV_KEY) return;
-    SetKeyCallback(ev->code, ev->value);
+    set_key_callback(charger, ev->code, ev->value);
 }
 
-void Charger::SetNextKeyCheck(key_state* key, int64_t timeout) {
+static void set_next_key_check(charger* charger, key_state* key, int64_t timeout) {
     int64_t then = key->timestamp + timeout;
 
-    if (next_key_check_ == -1 || then < next_key_check_) next_key_check_ = then;
+    if (charger->next_key_check == -1 || then < charger->next_key_check)
+        charger->next_key_check = then;
 }
 
-void Charger::ProcessKey(int code, int64_t now) {
-    key_state* key = &keys_[code];
+static void process_key(charger* charger, int code, int64_t now) {
+    key_state* key = &charger->keys[code];
 
     if (code == KEY_POWER) {
         if (key->down) {
@@ -466,7 +471,7 @@
                     LOGW("[%" PRId64 "] booting from charger mode\n", now);
                     property_set("sys.boot_from_charger_mode", "1");
                 } else {
-                    if (batt_anim_.cur_level >= boot_min_cap_) {
+                    if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
                         LOGW("[%" PRId64 "] rebooting\n", now);
                         reboot(RB_AUTOBOOT);
                     } else {
@@ -480,18 +485,18 @@
                 /* if the key is pressed but timeout hasn't expired,
                  * make sure we wake up at the right-ish time to check
                  */
-                SetNextKeyCheck(key, POWER_ON_KEY_TIME);
+                set_next_key_check(charger, key, POWER_ON_KEY_TIME);
 
                 /* Turn on the display and kick animation on power-key press
                  * rather than on key release
                  */
-                kick_animation(&batt_anim_);
+                kick_animation(charger->batt_anim);
                 request_suspend(false);
             }
         } else {
             /* if the power key got released, force screen state cycle */
             if (key->pending) {
-                kick_animation(&batt_anim_);
+                kick_animation(charger->batt_anim);
                 request_suspend(false);
             }
         }
@@ -500,35 +505,33 @@
     key->pending = false;
 }
 
-void Charger::HandleInputState(int64_t now) {
-    ProcessKey(KEY_POWER, now);
+static void handle_input_state(charger* charger, int64_t now) {
+    process_key(charger, KEY_POWER, now);
 
-    if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;
+    if (charger->next_key_check != -1 && now > charger->next_key_check)
+        charger->next_key_check = -1;
 }
 
-void Charger::HandlePowerSupplyState(int64_t now) {
-    int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;
-    if (!have_battery_state_) return;
+static void handle_power_supply_state(charger* charger, int64_t now) {
+    if (!charger->have_battery_state) return;
 
-    if (!charger_online()) {
+    if (!charger->charger_connected) {
         request_suspend(false);
-        if (next_pwr_check_ == -1) {
+        if (charger->next_pwr_check == -1) {
             /* 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 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);
-            next_screen_transition_ = now - 1;
-            reset_animation(&batt_anim_);
-            kick_animation(&batt_anim_);
-            next_pwr_check_ = now + timer_shutdown;
+            charger->next_screen_transition = now - 1;
+            reset_animation(charger->batt_anim);
+            kick_animation(charger->batt_anim);
+            charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
             LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
-                 now, (int64_t)timer_shutdown, next_pwr_check_);
-        } else if (now >= next_pwr_check_) {
+                 now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
+        } else if (now >= charger->next_pwr_check) {
             LOGW("[%" PRId64 "] shutting down\n", now);
             reboot(RB_POWER_OFF);
         } else {
@@ -536,60 +539,64 @@
         }
     } else {
         /* online supply present, reset shutdown timer if set */
-        if (next_pwr_check_ != -1) {
-            /* Reset next_screen_transition_ to update screen immediately.
+        if (charger->next_pwr_check != -1) {
+            /* Reset next_screen_transition to update screen immediately.
              * Reset & kick animation to show complete animation cycles
              * when charger connected again.
              */
             request_suspend(false);
-            next_screen_transition_ = now - 1;
-            reset_animation(&batt_anim_);
-            kick_animation(&batt_anim_);
+            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);
         }
-        next_pwr_check_ = -1;
+        charger->next_pwr_check = -1;
     }
 }
 
-void Charger::Heartbeat() {
-    // charger* charger = &charger_state;
+void healthd_mode_charger_heartbeat() {
+    charger* charger = &charger_state;
     int64_t now = curr_time_ms();
 
-    HandleInputState(now);
-    HandlePowerSupplyState(now);
+    handle_input_state(charger, now);
+    handle_power_supply_state(charger, now);
 
     /* do screen update last in case any of the above want to start
      * screen transitions (animations, etc)
      */
-    UpdateScreenState(now);
+    update_screen_state(charger, now);
 }
 
-void Charger::OnHealthInfoChanged(const HealthInfo_2_1& health_info) {
-    set_charger_online(health_info);
+void healthd_mode_charger_battery_update(android::BatteryProperties* props) {
+    charger* charger = &charger_state;
 
-    if (!have_battery_state_) {
-        have_battery_state_ = true;
-        next_screen_transition_ = curr_time_ms() - 1;
+    charger->charger_connected =
+        props->chargerAcOnline || props->chargerUsbOnline || props->chargerWirelessOnline;
+
+    if (!charger->have_battery_state) {
+        charger->have_battery_state = true;
+        charger->next_screen_transition = curr_time_ms() - 1;
         request_suspend(false);
-        reset_animation(&batt_anim_);
-        kick_animation(&batt_anim_);
+        reset_animation(charger->batt_anim);
+        kick_animation(charger->batt_anim);
     }
-    health_info_ = health_info.legacy.legacy;
-
-    AdjustWakealarmPeriods(charger_online());
+    batt_prop = props;
 }
 
-int Charger::PrepareToWait(void) {
+int healthd_mode_charger_preparetowait(void) {
+    charger* charger = &charger_state;
     int64_t now = curr_time_ms();
     int64_t next_event = INT64_MAX;
     int64_t timeout;
 
     LOGV("[%" PRId64 "] next screen: %" PRId64 " next key: %" PRId64 " next pwr: %" PRId64 "\n",
-         now, next_screen_transition_, next_key_check_, next_pwr_check_);
+         now, charger->next_screen_transition, charger->next_key_check, charger->next_pwr_check);
 
-    if (next_screen_transition_ != -1) next_event = next_screen_transition_;
-    if (next_key_check_ != -1 && next_key_check_ < next_event) next_event = next_key_check_;
-    if (next_pwr_check_ != -1 && next_pwr_check_ < next_event) next_event = next_pwr_check_;
+    if (charger->next_screen_transition != -1) next_event = charger->next_screen_transition;
+    if (charger->next_key_check != -1 && charger->next_key_check < next_event)
+        next_event = charger->next_key_check;
+    if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
+        next_event = charger->next_pwr_check;
 
     if (next_event != -1 && next_event != INT64_MAX)
         timeout = max(0, next_event - now);
@@ -599,32 +606,32 @@
     return (int)timeout;
 }
 
-int Charger::InputCallback(int fd, unsigned int epevents) {
+static int input_callback(charger* charger, int fd, unsigned int epevents) {
     input_event ev;
     int ret;
 
     ret = ev_get_input(fd, epevents, &ev);
     if (ret) return -1;
-    UpdateInputState(&ev);
+    update_input_state(charger, &ev);
     return 0;
 }
 
-static void charger_event_handler(HealthLoop* /*charger_loop*/, uint32_t /*epevents*/) {
+static void charger_event_handler(uint32_t /*epevents*/) {
     int ret;
 
     ret = ev_wait(-1);
     if (!ret) ev_dispatch();
 }
 
-void Charger::InitAnimation() {
+animation* init_animation() {
     bool parse_success;
 
     std::string content;
     if (base::ReadFileToString(product_animation_desc_path, &content)) {
-        parse_success = parse_animation_desc(content, &batt_anim_);
-        batt_anim_.set_resource_root(product_animation_root);
+        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, &batt_anim_);
+        parse_success = parse_animation_desc(content, &battery_animation);
     } else {
         LOGW("Could not open animation description at %s\n", animation_desc_path);
         parse_success = false;
@@ -632,36 +639,41 @@
 
     if (!parse_success) {
         LOGW("Could not parse animation description. Using default animation.\n");
-        batt_anim_ = BASE_ANIMATION;
-        batt_anim_.animation_file.assign("charger/battery_scale");
-        InitDefaultAnimationFrames();
-        batt_anim_.frames = owned_frames_.data();
-        batt_anim_.num_frames = owned_frames_.size();
+        battery_animation = BASE_ANIMATION;
+        battery_animation.animation_file.assign("charger/battery_scale");
+        battery_animation.frames = default_animation_frames;
+        battery_animation.num_frames = ARRAY_SIZE(default_animation_frames);
     }
-    if (batt_anim_.fail_file.empty()) {
-        batt_anim_.fail_file.assign("charger/battery_fail");
+    if (battery_animation.fail_file.empty()) {
+        battery_animation.fail_file.assign("charger/battery_fail");
     }
 
     LOGV("Animation Description:\n");
-    LOGV("  animation: %d %d '%s' (%d)\n", batt_anim_.num_cycles, batt_anim_.first_frame_repeats,
-         batt_anim_.animation_file.c_str(), batt_anim_.num_frames);
-    LOGV("  fail_file: '%s'\n", batt_anim_.fail_file.c_str());
-    LOGV("  clock: %d %d %d %d %d %d '%s'\n", batt_anim_.text_clock.pos_x,
-         batt_anim_.text_clock.pos_y, batt_anim_.text_clock.color_r, batt_anim_.text_clock.color_g,
-         batt_anim_.text_clock.color_b, batt_anim_.text_clock.color_a,
-         batt_anim_.text_clock.font_file.c_str());
-    LOGV("  percent: %d %d %d %d %d %d '%s'\n", batt_anim_.text_percent.pos_x,
-         batt_anim_.text_percent.pos_y, batt_anim_.text_percent.color_r,
-         batt_anim_.text_percent.color_g, batt_anim_.text_percent.color_b,
-         batt_anim_.text_percent.color_a, batt_anim_.text_percent.font_file.c_str());
-    for (int i = 0; i < batt_anim_.num_frames; i++) {
-        LOGV("  frame %.2d: %d %d %d\n", i, batt_anim_.frames[i].disp_time,
-             batt_anim_.frames[i].min_level, batt_anim_.frames[i].max_level);
+    LOGV("  animation: %d %d '%s' (%d)\n", battery_animation.num_cycles,
+         battery_animation.first_frame_repeats, battery_animation.animation_file.c_str(),
+         battery_animation.num_frames);
+    LOGV("  fail_file: '%s'\n", battery_animation.fail_file.c_str());
+    LOGV("  clock: %d %d %d %d %d %d '%s'\n", battery_animation.text_clock.pos_x,
+         battery_animation.text_clock.pos_y, battery_animation.text_clock.color_r,
+         battery_animation.text_clock.color_g, battery_animation.text_clock.color_b,
+         battery_animation.text_clock.color_a, battery_animation.text_clock.font_file.c_str());
+    LOGV("  percent: %d %d %d %d %d %d '%s'\n", battery_animation.text_percent.pos_x,
+         battery_animation.text_percent.pos_y, battery_animation.text_percent.color_r,
+         battery_animation.text_percent.color_g, battery_animation.text_percent.color_b,
+         battery_animation.text_percent.color_a, battery_animation.text_percent.font_file.c_str());
+    for (int i = 0; i < battery_animation.num_frames; i++) {
+        LOGV("  frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
+             battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
     }
+
+    return &battery_animation;
 }
 
-void Charger::Init(struct healthd_config* config) {
+void healthd_mode_charger_init(struct healthd_config* config) {
+    using android::hardware::health::V2_0::implementation::Health;
+
     int ret;
+    charger* charger = &charger_state;
     int i;
     int epollfd;
 
@@ -669,22 +681,22 @@
 
     LOGW("--------------- STARTING CHARGER MODE ---------------\n");
 
-    ret = ev_init(
-            std::bind(&Charger::InputCallback, this, std::placeholders::_1, std::placeholders::_2));
+    ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1, std::placeholders::_2));
     if (!ret) {
         epollfd = ev_get_epollfd();
-        RegisterEvent(epollfd, &charger_event_handler, EVENT_WAKEUP_FD);
+        healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
     }
 
-    InitAnimation();
+    animation* anim = init_animation();
+    charger->batt_anim = anim;
 
-    ret = res_create_display_surface(batt_anim_.fail_file.c_str(), &surf_unknown_);
+    ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
     if (ret < 0) {
         LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
-        ret = res_create_display_surface("charger/battery_fail", &surf_unknown_);
+        ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
         if (ret < 0) {
             LOGE("Cannot load built in battery_fail image\n");
-            surf_unknown_ = NULL;
+            charger->surf_unknown = NULL;
         }
     }
 
@@ -692,41 +704,49 @@
     int scale_count;
     int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
                     // chunk). We are using hard-coded frame.disp_time instead.
-    ret = res_create_multi_display_surface(batt_anim_.animation_file.c_str(), &scale_count,
-                                           &scale_fps, &scale_frames);
+    ret = res_create_multi_display_surface(anim->animation_file.c_str(), &scale_count, &scale_fps,
+                                           &scale_frames);
     if (ret < 0) {
         LOGE("Cannot load battery_scale image\n");
-        batt_anim_.num_frames = 0;
-        batt_anim_.num_cycles = 1;
-    } else if (scale_count != batt_anim_.num_frames) {
+        anim->num_frames = 0;
+        anim->num_cycles = 1;
+    } else if (scale_count != anim->num_frames) {
         LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
-             batt_anim_.num_frames);
-        batt_anim_.num_frames = 0;
-        batt_anim_.num_cycles = 1;
+             anim->num_frames);
+        anim->num_frames = 0;
+        anim->num_cycles = 1;
     } else {
-        for (i = 0; i < batt_anim_.num_frames; i++) {
-            batt_anim_.frames[i].surface = scale_frames[i];
+        for (i = 0; i < anim->num_frames; i++) {
+            anim->frames[i].surface = scale_frames[i];
         }
     }
-    ev_sync_key_state(std::bind(&Charger::SetKeyCallback, this, std::placeholders::_1,
-                                std::placeholders::_2));
+    ev_sync_key_state(
+        std::bind(&set_key_callback, charger, std::placeholders::_1, std::placeholders::_2));
 
-    next_screen_transition_ = -1;
-    next_key_check_ = -1;
-    next_pwr_check_ = -1;
-    wait_batt_level_timestamp_ = 0;
+    charger->next_screen_transition = -1;
+    charger->next_key_check = -1;
+    charger->next_pwr_check = -1;
+    charger->wait_batt_level_timestamp = 0;
 
-    // Retrieve healthd_config from the existing health HAL.
-    HalHealthLoop::Init(config);
+    // Initialize Health implementation (which initializes the internal BatteryMonitor).
+    Health::initInstance(config);
 
-    boot_min_cap_ = config->boot_min_cap;
+    healthd_config = config;
+    charger->boot_min_cap = config->boot_min_cap;
 }
 
-}  // namespace android
+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':
@@ -742,6 +762,5 @@
         }
     }
 
-    Charger charger(GetPassthroughHealth());
-    return charger.StartLoop();
+    return healthd_main();
 }
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
index 370ca86..2f0c9f2 100644
--- a/healthd/healthd_mode_charger.h
+++ b/healthd/healthd_mode_charger.h
@@ -16,72 +16,4 @@
 
 #pragma once
 
-#include <linux/input.h>
-
-#include <memory>
-#include <vector>
-
-#include <android/hardware/health/2.0/IHealthInfoCallback.h>
-#include <android/hardware/health/2.1/IHealth.h>
-#include <health2impl/HalHealthLoop.h>
-
-#include "animation.h"
-
-class GRSurface;
-class HealthdDraw;
-
-namespace android {
-struct key_state {
-    bool pending;
-    bool down;
-    int64_t timestamp;
-};
-
-class Charger : public ::android::hardware::health::V2_1::implementation::HalHealthLoop {
-  public:
-    using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
-    using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
-
-    Charger(const sp<android::hardware::health::V2_1::IHealth>& service);
-    ~Charger();
-
-  protected:
-    // HealthLoop overrides.
-    void Heartbeat() override;
-    int PrepareToWait() override;
-    void Init(struct healthd_config* config) override;
-    // HalHealthLoop overrides
-    void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
-
-  private:
-    void InitDefaultAnimationFrames();
-    void UpdateScreenState(int64_t now);
-    int SetKeyCallback(int code, int value);
-    void UpdateInputState(input_event* ev);
-    void SetNextKeyCheck(key_state* key, int64_t timeout);
-    void ProcessKey(int code, int64_t now);
-    void HandleInputState(int64_t now);
-    void HandlePowerSupplyState(int64_t now);
-    int InputCallback(int fd, unsigned int epevents);
-    void InitAnimation();
-
-    bool have_battery_state_ = false;
-    bool screen_blanked_ = false;
-    int64_t next_screen_transition_ = 0;
-    int64_t next_key_check_ = 0;
-    int64_t next_pwr_check_ = 0;
-    int64_t wait_batt_level_timestamp_ = 0;
-
-    key_state keys_[KEY_MAX + 1];
-
-    animation batt_anim_;
-    GRSurface* surf_unknown_ = nullptr;
-    int boot_min_cap_ = 0;
-
-    HealthInfo_1_0 health_info_ = {};
-    std::unique_ptr<HealthdDraw> healthd_draw_;
-    std::vector<animation::frame> owned_frames_;
-};
-}  // namespace android
-
 int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_nops.cpp b/healthd/healthd_mode_charger_nops.cpp
index 13e7348..bcc04d5 100644
--- a/healthd/healthd_mode_charger_nops.cpp
+++ b/healthd/healthd_mode_charger_nops.cpp
@@ -16,14 +16,45 @@
 
 #include "healthd_mode_charger_nops.h"
 
-#include <health2impl/HalHealthLoop.h>
+#include <health2/Health.h>
+#include <healthd/healthd.h>
 
-#include "charger_utils.h"
+#include <stdlib.h>
+#include <string.h>
 
-using android::hardware::health::GetPassthroughHealth;
-using android::hardware::health::V2_1::implementation::HalHealthLoop;
+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 */) {
-    HalHealthLoop charger("charger", GetPassthroughHealth());
-    return charger.StartLoop();
+    healthd_mode_ops = &healthd_nops;
+    return healthd_main();
 }
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index d41a374..4d1d53f 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -17,8 +17,6 @@
 #ifndef HEALTHD_BATTERYMONITOR_H
 #define HEALTHD_BATTERYMONITOR_H
 
-#include <memory>
-
 #include <batteryservice/BatteryService.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
@@ -26,19 +24,6 @@
 #include <healthd/healthd.h>
 
 namespace android {
-namespace hardware {
-namespace health {
-namespace V1_0 {
-struct HealthInfo;
-}  // namespace V1_0
-namespace V2_0 {
-struct HealthInfo;
-}  // namespace V2_0
-namespace V2_1 {
-struct HealthInfo;
-}  // namespace V2_1
-}  // namespace health
-}  // namespace hardware
 
 class BatteryMonitor {
   public:
@@ -52,19 +37,12 @@
     };
 
     BatteryMonitor();
-    ~BatteryMonitor();
     void init(struct healthd_config *hc);
+    bool update(void);
     int getChargeStatus();
     status_t getProperty(int id, struct BatteryProperty *val);
     void dumpState(int fd);
-
-    const android::hardware::health::V1_0::HealthInfo& getHealthInfo_1_0() const;
-    const android::hardware::health::V2_0::HealthInfo& getHealthInfo_2_0() const;
-    const android::hardware::health::V2_1::HealthInfo& getHealthInfo_2_1() const;
-
-    void updateValues(void);
-    void logValues(void);
-    bool isChargerOnline();
+    friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
 
   private:
     struct healthd_config *mHealthdConfig;
@@ -72,8 +50,10 @@
     bool mBatteryDevicePresent;
     int mBatteryFixedCapacity;
     int mBatteryFixedTemperature;
-    std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
+    struct BatteryProperties props;
 
+    int getBatteryStatus(const char* status);
+    int getBatteryHealth(const char* status);
     int readFromFile(const String8& path, std::string* buf);
     PowerSupplyType readPowerSupplyType(const String8& path);
     bool getBooleanField(const String8& path);
diff --git a/init/Android.bp b/init/Android.bp
index d939fcc..6be7290 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -26,7 +26,6 @@
         "-Wextra",
         "-Wno-unused-parameter",
         "-Werror",
-        "-DALLOW_FIRST_STAGE_CONSOLE=0",
         "-DALLOW_LOCAL_PROP_OVERRIDE=0",
         "-DALLOW_PERMISSIVE_SELINUX=0",
         "-DREBOOT_BOOTLOADER_ON_PANIC=0",
@@ -37,8 +36,6 @@
     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",
@@ -62,24 +59,25 @@
         },
     },
     static_libs: [
+        "libseccomp_policy",
         "libavb",
         "libc++fs",
         "libcgrouprc_format",
-        "liblmkd_utils",
-        "libmodprobe",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
-        "libsnapshot_init",
     ],
     shared_libs: [
         "libbacktrace",
         "libbase",
+        "libbinder",
         "libbootloader_message",
         "libcutils",
+        "libcrypto",
         "libdl",
         "libext4_utils",
         "libfs_mgr",
+        "libfscrypt",
         "libgsi",
         "libhidl-gen-utils",
         "libkeyutils",
@@ -92,34 +90,29 @@
         "libutils",
     ],
     bootstrap: true,
-    visibility: [":__subpackages__"],
 }
 
 cc_library_static {
     name: "libinit",
     recovery_available: true,
-    defaults: [
-        "init_defaults",
-        "selinux_policy_version",
-    ],
+    defaults: ["init_defaults", "selinux_policy_version"],
     srcs: [
         "action.cpp",
         "action_manager.cpp",
         "action_parser.cpp",
+        "boringssl_self_test.cpp",
         "bootchart.cpp",
         "builtins.cpp",
         "capabilities.cpp",
+        "descriptors.cpp",
         "devices.cpp",
         "epoll.cpp",
         "firmware_handler.cpp",
         "first_stage_init.cpp",
         "first_stage_mount.cpp",
-        "fscrypt_init_extensions.cpp",
         "import_parser.cpp",
         "init.cpp",
-        "interface_utils.cpp",
         "keychords.cpp",
-        "lmkd_service.cpp",
         "modalias_handler.cpp",
         "mount_handler.cpp",
         "mount_namespace.cpp",
@@ -127,17 +120,12 @@
         "persistent_properties.cpp",
         "persistent_properties.proto",
         "property_service.cpp",
-        "property_service.proto",
         "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",
@@ -149,10 +137,7 @@
         "ueventd_parser.cpp",
         "util.cpp",
     ],
-    whole_static_libs: [
-        "libcap",
-        "com.android.sysprop.apex",
-    ],
+    whole_static_libs: ["libcap", "com.android.sysprop.apex"],
     header_libs: ["bootimg_headers"],
     proto: {
         type: "lite",
@@ -162,21 +147,11 @@
     target: {
         recovery: {
             cflags: ["-DRECOVERY"],
-            exclude_shared_libs: [
-                "libbinder",
-                "libutils",
-            ],
+            exclude_shared_libs: ["libbinder", "libutils"],
         },
     },
 }
 
-phony {
-    name: "init",
-    required: [
-        "init_second_stage",
-    ],
-}
-
 cc_binary {
     name: "init_second_stage",
     recovery_available: true,
@@ -194,40 +169,27 @@
     target: {
         recovery: {
             cflags: ["-DRECOVERY"],
-            exclude_shared_libs: [
-                "libbinder",
-                "libutils",
-            ],
+            exclude_shared_libs: ["libbinder", "libutils"],
         },
     },
+    ldflags: ["-Wl,--rpath,/system/${LIB}/bootstrap"],
 }
 
 // Tests
 // ------------------------------------------------------------------------------
 
 cc_test {
-    name: "CtsInitTestCases",
+    name: "init_tests",
     defaults: ["init_defaults"],
-    require_root: true,
-
-    compile_multilib: "both",
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
-        },
-    },
-
+    compile_multilib: "first",
     srcs: [
         "devices_test.cpp",
-        "firmware_handler_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",
@@ -237,12 +199,7 @@
         "util_test.cpp",
     ],
     static_libs: ["libinit"],
-
-    test_suites: [
-        "cts",
-        "device-tests",
-        "vts",
-    ],
+    test_suites: ["device-tests"],
 }
 
 cc_benchmark {
@@ -259,13 +216,9 @@
 
 genrule {
     name: "generated_stub_builtin_function_map",
-    tool_files: ["host_builtin_map.py"],
     out: ["generated_stub_builtin_function_map.h"],
-    srcs: [
-        "builtins.cpp",
-        "check_builtins.cpp",
-    ],
-    cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
+    srcs: ["builtins.cpp"],
+    cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)",
 }
 
 cc_binary {
@@ -284,32 +237,28 @@
     ],
     whole_static_libs: ["libcap"],
     shared_libs: [
-        "libcutils",
-        "libhidl-gen-utils",
-        "libhidlmetadata",
-        "liblog",
-        "libprocessgroup",
         "libprotobuf-cpp-lite",
+        "libhidl-gen-utils",
+        "libprocessgroup",
+        "liblog",
+        "libcutils",
     ],
     srcs: [
         "action.cpp",
         "action_manager.cpp",
         "action_parser.cpp",
         "capabilities.cpp",
-        "check_builtins.cpp",
+        "descriptors.cpp",
         "epoll.cpp",
         "keychords.cpp",
         "import_parser.cpp",
-        "interface_utils.cpp",
         "host_import_parser.cpp",
         "host_init_verifier.cpp",
+        "host_init_stubs.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",
@@ -319,7 +268,7 @@
     },
     generated_headers: [
         "generated_stub_builtin_function_map",
-        "generated_android_ids",
+        "generated_android_ids"
     ],
     target: {
         android: {
@@ -330,3 +279,5 @@
         },
     },
 }
+
+subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index 997b2bc..c4f7d34 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -8,7 +8,6 @@
 
 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 \
@@ -16,7 +15,6 @@
     -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 \
@@ -54,7 +52,6 @@
     first_stage_mount.cpp \
     mount_namespace.cpp \
     reboot_utils.cpp \
-    selabel.cpp \
     selinux.cpp \
     switch_root.cpp \
     uevent_listener.cpp \
@@ -91,6 +88,8 @@
     libsquashfs_utils \
     liblogwrap \
     libext4_utils \
+    libfscrypt \
+    libseccomp_policy \
     libcrypto_utils \
     libsparse \
     libavb \
@@ -99,7 +98,7 @@
     libcutils \
     libbase \
     liblog \
-    libcrypto_static \
+    libcrypto \
     libdl \
     libz \
     libselinux \
@@ -107,13 +106,9 @@
     libgsi \
     libcom.android.sysprop.apex \
     liblzma \
-    libdexfile_support_static \
+    libdexfile_support \
     libunwindstack \
     libbacktrace \
-    libmodprobe \
-    libext2_uuid \
-    libprotobuf-cpp-lite \
-    libsnapshot_init \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
deleted file mode 100644
index 667911d..0000000
--- a/init/AndroidTest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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 CTS init 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="not_multi_abi" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
-        <option name="cleanup" value="true" />
-        <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
-        <option name="append-bitness" value="true" />
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="CtsInitTestCases" />
-        <option name="runtime-hint" value="65s" />
-    </test>
-</configuration>
diff --git a/init/README.md b/init/README.md
index e8df4ec..1b8b848 100644
--- a/init/README.md
+++ b/init/README.md
@@ -35,19 +35,29 @@
 at the beginning of its execution.  It is responsible for the initial
 set up of the system.
 
-Init loads all of the files contained within the
+Devices that mount /system, /vendor through the first stage mount mechanism
+load 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 previously were
-able to import init scripts during mount_all, however that is deprecated
-and not allowed for devices launching after Q.
+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.
 
 The intention of these directories is:
 
    1. /system/etc/init/ is for core system items such as
-      SurfaceFlinger, MediaService, and logd.
+      SurfaceFlinger, MediaService, and logcatd.
    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
@@ -62,7 +72,7 @@
 init .rc file should additionally contain any actions associated with
 its service.
 
-An example is the userdebug logcatd.rc and Android.mk files located in the
+An example is the 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
@@ -78,6 +88,14 @@
 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
@@ -170,8 +188,6 @@
   be changed by setting the "androidboot.console" kernel parameter. In
   all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
   specified as just "console tty0".
-  This option connects stdin, stdout, and stderr to the console. It is mutually exclusive with the
-  stdio_to_kmsg option, which only connects stdout and stderr to kmsg.
 
 `critical`
 > This is a device-critical service. If it exits more than four times in
@@ -198,9 +214,9 @@
 
 `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.
+  must be a fully-qualified name and not a value name. 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>`
@@ -265,12 +281,6 @@
 > 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().
 
-`reboot_on_failure <target>`
-> If this process cannot be started or if the process terminates with an exit code other than
-  CLD_EXITED or an status other than '0', reboot the system with the target specified in
-  _target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
-  intended to be used with the `exec_start` builtin for any must-have checks during boot.
-
 `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.
@@ -307,21 +317,13 @@
   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".  _type_
-  may end with "+passcred" to enable SO_PASSCRED on the socket. User and
+> 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().
 
-`stdio_to_kmsg`
-> Redirect stdout and stderr to /dev/kmsg_debug. This is useful for services that do not use native
-  Android logging during early boot and whose logs messages we want to capture. This is only enabled
-  when /dev/kmsg_debug is enabled, which is only enabled on userdebug and eng builds.
-  This is mutually exclusive with the console option, which additionally connects stdin to the
-  given console.
-
 `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.
@@ -495,49 +497,32 @@
   This is included in the default init.rc.
 
 `loglevel <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_.
+> Sets the kernel log level to level. 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>] [encryption=<action>] [key=<key>]`
+`mkdir <path> [mode] [owner] [group]`
 > Create a directory at _path_, optionally with the given mode, owner, and
   group. If not provided, the directory is created with permissions 755 and
   owned by the root user and root group. If provided, the mode, owner and group
   will be updated if the directory exists already.
 
- > _action_ can be one of:
-  * `None`: take no encryption action; directory will be encrypted if parent is.
-  * `Require`: encrypt directory, abort boot process if encryption fails
-  * `Attempt`: try to set an encryption policy, but continue if it fails
-  * `DeleteIfNecessary`: recursively delete directory if necessary to set
-  encryption policy.
-
-  > _key_ can be one of:
-  * `ref`: use the systemwide DE key
-  * `per_boot_ref`: use the key freshly generated on each boot.
-
 `mount_all <fstab> [ <path> ]\* [--<option>]`
-> Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
+> 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
   options "early" and "late".
-  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.
+  Refer to the section of "Init .rc Files" for detail.
 
 `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, e.g. barrier=1,noauto\_da\_alloc
+  a comma separated string, eg: barrier=1,noauto\_da\_alloc
 
 `parse_apex_configs`
-> Parses config file(s) from the mounted APEXes. Intended to be used only once
+> Parses config file(s) from the mounted APEXes. Intented to be used only once
   when apexd notifies the mount event by setting apexd.status to ready.
 
 `restart <service>`
@@ -600,7 +585,7 @@
 `symlink <target> <path>`
 > Create a symbolic link at _path_ with the value _target_
 
-`sysclktz <minutes_west_of_gmt>`
+`sysclktz <mins_west_of_gmt>`
 > Set the system clock base (0 if system clock ticks in GMT)
 
 `trigger <event>`
@@ -610,6 +595,9 @@
 `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
@@ -649,9 +637,8 @@
       `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. (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.
+   3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+      paths during mount_all.
 
 The order that files are imported is a bit complex for legacy reasons
 and to keep backwards compatibility.  It is not strictly guaranteed.
@@ -661,7 +648,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 de facto order for first stage mount devices is:
+Nonetheless, the defacto 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
@@ -708,11 +695,8 @@
 > 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 in ns it took to run SELinux stage.
+> How long it took the first stage to initialize SELinux.
 
 `ro.boottime.init.cold_boot_wait`
 > How long init waited for ueventd's coldboot phase to end.
@@ -754,7 +738,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 tarballs are preserved on the host machine under different
+If two such barballs 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_
@@ -795,12 +779,6 @@
 
 Debugging init
 --------------
-When a service starts from init, it may fail to `execv()` the service. This is not typical, and may
-point to an error happening in the linker as the new service is started. The linker in Android
-prints its logs to `logd` and `stderr`, so they are visible in `logcat`. If the error is encountered
-before it is possible to access `logcat`, the `stdio_to_kmsg` service option may be used to direct
-the logs that the linker prints to `stderr` to `kmsg`, where they can be read via a serial port.
-
 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.
 
@@ -808,7 +786,7 @@
 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.
 
-This flag can also be dynamically controlled via the ctl.sigstop_on and ctl.sigstop_off properties.
+This flag can also be dynamically controled via the ctl.sigstop_on and ctl.sigstop_off properties.
 
 Below is an example of dynamically debugging logd via the above:
 
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
deleted file mode 100644
index 053ebf8..0000000
--- a/init/README.ueventd.md
+++ /dev/null
@@ -1,138 +0,0 @@
-# Ueventd
--------
-Ueventd manages `/dev`, sets permissions for `/sys`, and handles firmware uevents. It has default
-behavior described below, along with a scripting language that allows customizing this behavior,
-built on the same parser as init.
-
-Ueventd has one generic customization parameter, the size of rcvbuf_size for the ueventd socket. It
-is customized by the `uevent_socket_rcvbuf_size` parameter, which takes the format of
-
-    uevent_socket_rcvbuf_size <size>
-For example
-
-    uevent_socket_rcvbuf_size 16M
-Sets the uevent socket rcvbuf_size to 16 megabytes.
-
-## /dev
-----
-Ueventd listens to the kernel uevent sockets and creates/deletes nodes in `/dev` based on the
-incoming add/remove uevents. It defaults to using `0600` mode and `root` user/group. It always
-creates the nodes with the SELabel from the current loaded SEPolicy. It has three default behaviors
-for the node path:
-
-  1. Block devices are created as `/dev/block/<basename uevent DEVPATH>`. There are symlinks created
-     to this node at `/dev/block/<type>/<parent device>/<basename uevent DEVPATH>`,
-     `/dev/block/<type>/<parent device>/by-name/<uevent PARTNAME>`, and `/dev/block/by-name/<uevent
-     PARTNAME>` if the device is a boot device.
-  2. USB devices are created as `/dev/<uevent DEVNAME>` if `DEVNAME` was specified for the uevent,
-     otherwise as `/dev/bus/usb/<bus_id>/<device_id>` where `bus_id` is `uevent MINOR / 128 + 1` and
-     `device_id` is `uevent MINOR % 128 + 1`.
-  3. All other devices are created as `/dev/<basename uevent DEVPATH>`
-
-The permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These
-lines take the format of
-
-    devname mode uid gid
-For example
-
-    /dev/null 0666 root root
-When `/dev/null` is created, its mode will be set to `0666`, its user to `root` and its group to
-`root`.
-
-The path can be modified using a ueventd.rc script and a `subsystem` section. There are three to set
-for a subsystem: the subsystem name, which device name to use, and which directory to place the
-device in. The section takes the below format of
-
-    subsystem <subsystem_name>
-      devname uevent_devname|uevent_devpath
-      [dirname <directory>]
-
-`subsystem_name` is used to match uevent `SUBSYSTEM` value
-
-`devname` takes one of two options
-  1. `uevent_devname` specifies that the name of the node will be the uevent `DEVNAME`
-  2. `uevent_devpath` specified that the name of the node will be basename uevent `DEVPATH`
-
-`dirname` is an optional parameter that specifies a directory within `/dev` where the node will be
-created.
-
-For example
-
-    subsystem sound
-      devname uevent_devpath
-      dirname /dev/snd
-Indicates that all uevents with `SUBSYSTEM=sound` will create nodes as `/dev/snd/<basename uevent
-DEVPATH>`.
-
-## /sys
-----
-Ueventd by default takes no action for `/sys`, however it can be instructed to set permissions for
-certain files in `/sys` when matching uevents are generated. This is done using a ueventd.rc script
-and a line that begins with `/sys`. These lines take the format of
-
-    nodename attr mode uid gid
-For example
-
-    /sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
-When a uevent that matches the pattern `/sys/devices/system/cpu/cpu*` is sent, the matching sysfs
-attribute, `cpufreq/scaling_max_freq`, will have its mode set to `0664`, its user to to `system` and
-its group set to `system`.
-
-Note that `*` matches as a wildcard and can be used anywhere in a path.
-
-## Firmware loading
-----------------
-Ueventd by default serves firmware requests by searching through a list of firmware directories
-for a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the
-kernel.
-
-The list of firmware directories is customized by a `firmware_directories` line in a ueventd.rc
-file. This line takes the format of
-
-    firmware_directories <firmware_directory> [ <firmware_directory> ]*
-For example
-
-    firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
-Adds those 4 directories, in that order to the list of firmware directories that will be tried by
-ueventd. Note that this option always accumulates to the list; it is not possible to remove previous
-entries.
-
-Ueventd will wait until after `post-fs` in init, to keep retrying before believing the firmwares are
-not present.
-
-The exact firmware file to be served can be customized by running an external program by a
-`external_firmware_handler` line in a ueventd.rc file. This line takes the format of
-
-    external_firmware_handler <devpath> <user name to run as> <path to external program>
-For example
-
-    external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin
-Will launch `/vendor/bin/led_coeffs.bin` as the system user instead of serving the default firmware
-for `/devices/leds/red/firmware/coeffs.bin`.
-
-Ueventd will provide the uevent `DEVPATH` and `FIRMWARE` to this external program on the environment
-via environment variables with the same names. Ueventd will use the string written to stdout as the
-new name of the firmware to load. It will still look for the new firmware in the list of firmware
-directories stated above. It will also reject file names with `..` in them, to prevent leaving these
-directories. If stdout cannot be read, or the program returns with any exit code other than
-`EXIT_SUCCESS`, or the program crashes, the default firmware from the uevent will be loaded.
-
-Ueventd will additionally log all messages sent to stderr from the external program to the serial
-console after the external program has exited.
-
-## Coldboot
---------
-Ueventd must create devices in `/dev` for all devices that have already sent their uevents before
-ueventd has started. To do so, when ueventd is started it does what it calls a 'coldboot' on `/sys`,
-in which it writes 'add' to every 'uevent' file that it finds in `/sys/class`, `/sys/block`, and
-`/sys/devices`. This causes the kernel to regenerate the uevents for these paths, and thus for
-ueventd to create the nodes.
-
-For boot time purposes, this is done in parallel across a set of child processes. `ueventd.cpp` in
-this directory contains documentation on how the parallelization is done.
-
-There is an option to parallelize the restorecon function during cold boot as well. This should only
-be done for devices that do not use genfscon, which is the recommended method for labeling sysfs
-nodes. To enable this option, use the below line in a ueventd.rc script:
-
-    parallel_restorecon enabled
diff --git a/init/action.cpp b/init/action.cpp
index 69e40d0..94ccef2 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -28,18 +28,17 @@
 namespace android {
 namespace init {
 
-Result<void> RunBuiltinFunction(const BuiltinFunction& function,
-                                const std::vector<std::string>& args, const std::string& context) {
+Result<Success> 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());
     builtin_arguments.args[0] = args[0];
     for (std::size_t i = 1; i < args.size(); ++i) {
-        auto expanded_arg = ExpandProps(args[i]);
-        if (!expanded_arg) {
-            return expanded_arg.error();
+        if (!expand_props(args[i], &builtin_arguments.args[i])) {
+            return Error() << "cannot expand '" << args[i] << "'";
         }
-        builtin_arguments.args[i] = std::move(*expanded_arg);
     }
 
     return function(builtin_arguments);
@@ -52,7 +51,7 @@
       args_(std::move(args)),
       line_(line) {}
 
-Result<void> Command::InvokeFunc(Subcontext* subcontext) const {
+Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
     if (subcontext) {
         if (execute_in_subcontext_) {
             return subcontext->Execute(args_);
@@ -68,30 +67,6 @@
     return RunBuiltinFunction(func_, args_, kInitContext);
 }
 
-Result<void> Command::CheckCommand() const {
-    auto builtin_arguments = BuiltinArguments("host_init_verifier");
-
-    builtin_arguments.args.resize(args_.size());
-    builtin_arguments.args[0] = args_[0];
-    for (size_t i = 1; i < args_.size(); ++i) {
-        auto expanded_arg = ExpandProps(args_[i]);
-        if (!expanded_arg) {
-            if (expanded_arg.error().message().find("doesn't exist while expanding") !=
-                std::string::npos) {
-                // If we failed because we won't have a property, use an empty string, which is
-                // never returned from the parser, to indicate that this field cannot be checked.
-                builtin_arguments.args[i] = "";
-            } else {
-                return expanded_arg.error();
-            }
-        } else {
-            builtin_arguments.args[i] = std::move(*expanded_arg);
-        }
-    }
-
-    return func_(builtin_arguments);
-}
-
 std::string Command::BuildCommandString() const {
     return Join(args_, ' ');
 }
@@ -106,43 +81,28 @@
       filename_(filename),
       line_(line) {}
 
-const BuiltinFunctionMap* Action::function_map_ = nullptr;
+const KeywordFunctionMap* Action::function_map_ = nullptr;
 
-Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
+Result<Success> Action::AddCommand(std::vector<std::string>&& args, int line) {
     if (!function_map_) {
         return Error() << "no function map available";
     }
 
-    auto map_result = function_map_->Find(args);
-    if (!map_result) {
-        return Error() << map_result.error();
-    }
+    auto function = function_map_->FindFunction(args);
+    if (!function) return Error() << function.error();
 
-    commands_.emplace_back(map_result->function, map_result->run_in_subcontext, std::move(args),
-                           line);
-    return {};
+    commands_.emplace_back(function->second, function->first, std::move(args), line);
+    return Success();
 }
 
 void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
-    commands_.emplace_back(std::move(f), false, std::move(args), line);
+    commands_.emplace_back(f, false, std::move(args), line);
 }
 
 std::size_t Action::NumCommands() const {
     return commands_.size();
 }
 
-size_t Action::CheckAllCommands() const {
-    size_t failures = 0;
-    for (const auto& command : commands_) {
-        if (auto result = command.CheckCommand(); !result) {
-            LOG(ERROR) << "Command '" << command.BuildCommandString() << "' (" << filename_ << ":"
-                       << command.line() << ") failed: " << result.error();
-            ++failures;
-        }
-    }
-    return failures;
-}
-
 void Action::ExecuteOneCommand(std::size_t command) const {
     // We need a copy here since some Command execution may result in
     // changing commands_ vector by importing .rc files through parser
@@ -161,15 +121,25 @@
     auto result = command.InvokeFunc(subcontext_);
     auto duration = t.duration();
 
+    // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
+    // device, such as '/sys/class/leds/jogball-backlight/brightness'.  As of this writing, there
+    // are 198 such failures on bullhead.  Instead of spamming the log reporting them, we do not
+    // report such failures unless we're running at the DEBUG log level.
+    bool report_failure = !result.has_value();
+    if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
+        result.error_errno() == ENOENT) {
+        report_failure = false;
+    }
+
     // Any action longer than 50ms will be warned to user as slow operation
-    if (!result.has_value() || duration > 50ms ||
+    if (report_failure || duration > 50ms ||
         android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
 
         LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
                   << ":" << command.line() << ") took " << duration.count() << "ms and "
-                  << (result ? "succeeded" : "failed: " + result.error().message());
+                  << (result ? "succeeded" : "failed: " + result.error_string());
     }
 }
 
@@ -195,11 +165,10 @@
                 found = true;
             }
         } else {
-            std::string prop_value = android::base::GetProperty(trigger_name, "");
-            if (trigger_value == "*" && !prop_value.empty()) {
-                continue;
+            std::string prop_val = android::base::GetProperty(trigger_name, "");
+            if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
+                return false;
             }
-            if (trigger_value != prop_value) return false;
         }
     }
     return found;
diff --git a/init/action.h b/init/action.h
index 1534bf9..967c682 100644
--- a/init/action.h
+++ b/init/action.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_ACTION_H
+#define _INIT_ACTION_H
 
 #include <map>
 #include <queue>
@@ -30,17 +31,16 @@
 namespace android {
 namespace init {
 
-Result<void> RunBuiltinFunction(const BuiltinFunction& function,
-                                const std::vector<std::string>& args, const std::string& context);
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+                                   const std::vector<std::string>& args, const std::string& context);
 
 class Command {
   public:
     Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
             int line);
 
-    Result<void> InvokeFunc(Subcontext* subcontext) const;
+    Result<Success> InvokeFunc(Subcontext* subcontext) const;
     std::string BuildCommandString() const;
-    Result<void> CheckCommand() const;
 
     int line() const { return line_; }
 
@@ -61,9 +61,9 @@
            const std::string& event_trigger,
            const std::map<std::string, std::string>& property_triggers);
 
-    Result<void> AddCommand(std::vector<std::string>&& args, int line);
+    Result<Success> AddCommand(std::vector<std::string>&& args, int line);
     void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);
-    size_t NumCommands() const;
+    std::size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
     bool CheckEvent(const EventTrigger& event_trigger) const;
@@ -71,12 +71,11 @@
     bool CheckEvent(const BuiltinAction& builtin_action) const;
     std::string BuildTriggersString() const;
     void DumpState() const;
-    size_t CheckAllCommands() const;
 
     bool oneshot() const { return oneshot_; }
     const std::string& filename() const { return filename_; }
     int line() const { return line_; }
-    static void set_function_map(const BuiltinFunctionMap* function_map) {
+    static void set_function_map(const KeywordFunctionMap* function_map) {
         function_map_ = function_map;
     }
 
@@ -92,8 +91,10 @@
     Subcontext* subcontext_;
     std::string filename_;
     int line_;
-    static const BuiltinFunctionMap* function_map_;
+    static const KeywordFunctionMap* function_map_;
 };
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index ebca762..9de4085 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -23,14 +23,6 @@
 
 ActionManager::ActionManager() : current_command_(0) {}
 
-size_t ActionManager::CheckAllCommands() {
-    size_t failures = 0;
-    for (const auto& action : actions_) {
-        failures += action->CheckAllCommands();
-    }
-    return failures;
-}
-
 ActionManager& ActionManager::GetInstance() {
     static ActionManager instance;
     return instance;
@@ -55,7 +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>{});
-    action->AddCommand(std::move(func), {name}, 0);
+    action->AddCommand(func, {name}, 0);
 
     event_queue_.emplace(action.get());
     actions_.emplace_back(std::move(action));
@@ -96,8 +88,7 @@
         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_.end());
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
         }
     }
 }
diff --git a/init/action_manager.h b/init/action_manager.h
index a2b95ac..5f47a6d 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_ACTION_MANAGER_H
+#define _INIT_ACTION_MANAGER_H
 
 #include <string>
 #include <vector>
@@ -31,7 +32,6 @@
 
     // Exposed for testing
     ActionManager();
-    size_t CheckAllCommands();
 
     void AddAction(std::unique_ptr<Action> action);
     void QueueEventTrigger(const std::string& trigger);
@@ -55,3 +55,5 @@
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index a8e1e09..4f8bd16 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,14 +16,11 @@
 
 #include "action_parser.h"
 
-#include <ctype.h>
-
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 
 #if defined(__ANDROID__)
 #include "property_service.h"
-#include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
@@ -58,8 +55,8 @@
     return CanReadProperty(subcontext->context(), prop_name);
 }
 
-Result<void> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
-                                  std::map<std::string, std::string>* property_triggers) {
+Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
+                                     std::map<std::string, std::string>* property_triggers) {
     const static std::string prop_str("property:");
     std::string prop_name(trigger.substr(prop_str.length()));
     size_t equal_pos = prop_name.find('=');
@@ -77,23 +74,12 @@
     if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
         return Error() << "multiple property triggers found for same property";
     }
-    return {};
+    return Success();
 }
 
-Result<void> ValidateEventTrigger(const std::string& event_trigger) {
-    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
-        for (const char& c : event_trigger) {
-            if (c != '_' && c != '-' && !std::isalnum(c)) {
-                return Error() << "Illegal character '" << c << "' in '" << event_trigger << "'";
-            }
-        }
-    }
-    return {};
-}
-
-Result<void> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
-                           std::string* event_trigger,
-                           std::map<std::string, std::string>* property_triggers) {
+Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
+                              std::string* event_trigger,
+                              std::map<std::string, std::string>* property_triggers) {
     const static std::string prop_str("property:");
     for (std::size_t i = 0; i < args.size(); ++i) {
         if (args[i].empty()) {
@@ -117,29 +103,31 @@
             if (!event_trigger->empty()) {
                 return Error() << "multiple event triggers are not allowed";
             }
-            if (auto result = ValidateEventTrigger(args[i]); !result) {
-                return result;
-            }
 
             *event_trigger = args[i];
         }
     }
 
-    return {};
+    return Success();
 }
 
 }  // namespace
 
-Result<void> ActionParser::ParseSection(std::vector<std::string>&& args,
-                                        const std::string& filename, int line) {
+Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
+                                           const std::string& filename, int line) {
     std::vector<std::string> triggers(args.begin() + 1, args.end());
     if (triggers.size() < 1) {
         return Error() << "Actions must have a trigger";
     }
 
     Subcontext* action_subcontext = nullptr;
-    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
-        action_subcontext = subcontext_;
+    if (subcontexts_) {
+        for (auto& subcontext : *subcontexts_) {
+            if (StartsWith(filename, subcontext.path_prefix())) {
+                action_subcontext = &subcontext;
+                break;
+            }
+        }
     }
 
     std::string event_trigger;
@@ -154,19 +142,19 @@
                                            property_triggers);
 
     action_ = std::move(action);
-    return {};
+    return 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::ParseLineSection(std::vector<std::string>&& args, int line) {
+    return action_ ? action_->AddCommand(std::move(args), line) : Success();
 }
 
-Result<void> ActionParser::EndSection() {
+Result<Success> ActionParser::EndSection() {
     if (action_ && action_->NumCommands() > 0) {
         action_manager_->AddAction(std::move(action_));
     }
 
-    return {};
+    return Success();
 }
 
 }  // namespace init
diff --git a/init/action_parser.h b/init/action_parser.h
index 3000132..b7f7074 100644
--- a/init/action_parser.h
+++ b/init/action_parser.h
@@ -30,16 +30,16 @@
 
 class ActionParser : public SectionParser {
   public:
-    ActionParser(ActionManager* action_manager, Subcontext* subcontext)
-        : action_manager_(action_manager), subcontext_(subcontext), action_(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;
+    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
+        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
+    Result<Success> EndSection() override;
 
   private:
     ActionManager* action_manager_;
-    Subcontext* subcontext_;
+    std::vector<Subcontext>* subcontexts_;
     std::unique_ptr<Action> action_;
 };
 
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index b7db9b6..c2cf573 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -165,20 +165,20 @@
   LOG(INFO) << "Bootcharting finished";
 }
 
-static Result<void> do_bootchart_start() {
+static Result<Success> do_bootchart_start() {
     // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
     std::string start;
     if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
         LOG(VERBOSE) << "Not bootcharting";
-        return {};
+        return Success();
     }
 
     g_bootcharting_thread = new std::thread(bootchart_thread_main);
-    return {};
+    return Success();
 }
 
-static Result<void> do_bootchart_stop() {
-    if (!g_bootcharting_thread) return {};
+static Result<Success> do_bootchart_stop() {
+    if (!g_bootcharting_thread) return Success();
 
     // Tell the worker thread it's time to quit.
     {
@@ -190,10 +190,10 @@
     g_bootcharting_thread->join();
     delete g_bootcharting_thread;
     g_bootcharting_thread = nullptr;
-    return {};
+    return Success();
 }
 
-Result<void> do_bootchart(const BuiltinArguments& args) {
+Result<Success> 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 6f19aad..05474ca 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -26,7 +26,7 @@
 namespace android {
 namespace init {
 
-Result<void> do_bootchart(const BuiltinArguments& args);
+Result<Success> do_bootchart(const BuiltinArguments& args);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/boringssl_self_test.cpp b/init/boringssl_self_test.cpp
new file mode 100644
index 0000000..0408d30
--- /dev/null
+++ b/init/boringssl_self_test.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "boringssl_self_test.h"
+
+#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
+#include <openssl/crypto.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace init {
+
+Result<Success> StartBoringSslSelfTest(const BuiltinArguments&) {
+    pid_t id = fork();
+
+    if (id == 0) {
+        if (BORINGSSL_self_test() != 1) {
+            LOG(INFO) << "BoringSSL crypto self tests failed";
+
+            // This check has failed, so the device should refuse
+            // 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");
+            if (result != 0) {
+                LOG(ERROR) << "Failed to reboot into bootloader";
+            }
+        }
+
+        _exit(0);
+    } else if (id == -1) {
+        // Failed to fork, so cannot run the test. Refuse to continue.
+        PLOG(FATAL) << "Failed to fork for BoringSSL self test";
+    }
+
+    return Success();
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/healthd/charger_utils.h b/init/boringssl_self_test.h
similarity index 72%
rename from healthd/charger_utils.h
rename to init/boringssl_self_test.h
index f96e827..b21fc78 100644
--- a/healthd/charger_utils.h
+++ b/init/boringssl_self_test.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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.
@@ -16,12 +16,13 @@
 
 #pragma once
 
-#include <android/hardware/health/2.1/IHealth.h>
+#include "builtin_arguments.h"
+#include "result.h"
 
 namespace android {
-namespace hardware {
-namespace health {
-sp<V2_1::IHealth> GetPassthroughHealth();
-}  // namespace health
-}  // namespace hardware
+namespace init {
+
+Result<Success> StartBoringSslSelfTest(const BuiltinArguments&);
+
+}  // namespace init
 }  // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 5ee928e..25f324c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -16,7 +16,6 @@
 
 #include "builtins.h"
 
-#include <android/api-level.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -42,9 +41,6 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <memory>
-
-#include <ApexProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -57,6 +53,7 @@
 #include <cutils/android_reboot.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>
@@ -65,25 +62,20 @@
 
 #include "action_manager.h"
 #include "bootchart.h"
-#include "fscrypt_init_extensions.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::StartsWith;
-using android::base::StringPrintf;
 using android::base::unique_fd;
 using android::fs_mgr::Fstab;
 using android::fs_mgr::ReadFstabFromFile;
@@ -93,62 +85,16 @@
 namespace android {
 namespace init {
 
-// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
-// device, such as '/sys/class/leds/jogball-backlight/brightness'.  As of this writing, there
-// are 81 such failures on cuttlefish.  Instead of spamming the log reporting them, we do not
-// report such failures unless we're running at the DEBUG log level.
-class ErrorIgnoreEnoent {
-  public:
-    ErrorIgnoreEnoent()
-        : ignore_error_(errno == ENOENT &&
-                        android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
-    explicit ErrorIgnoreEnoent(int errno_to_append)
-        : error_(errno_to_append),
-          ignore_error_(errno_to_append == ENOENT &&
-                        android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
-
-    template <typename T>
-    operator android::base::expected<T, ResultError>() {
-        if (ignore_error_) {
-            return {};
-        }
-        return error_;
-    }
-
-    template <typename T>
-    ErrorIgnoreEnoent& operator<<(T&& t) {
-        error_ << t;
-        return *this;
-    }
-
-  private:
-    Error error_;
-    bool ignore_error_;
-};
-
-inline ErrorIgnoreEnoent ErrnoErrorIgnoreEnoent() {
-    return ErrorIgnoreEnoent(errno);
-}
-
-std::vector<std::string> late_import_paths;
-
 static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
 
-static Result<void> reboot_into_recovery(const std::vector<std::string>& options) {
+static Result<Success> reboot_into_recovery(const std::vector<std::string>& options) {
     LOG(ERROR) << "Rebooting into recovery";
     std::string err;
     if (!write_bootloader_message(options, &err)) {
         return Error() << "Failed to set bootloader message: " << err;
     }
-    // This function should only be reached from init and not from vendor_init, and we want to
-    // immediately trigger reboot instead of relaying through property_service.  Older devices may
-    // still have paths that reach here from vendor_init, so we keep the property_set as a fallback.
-    if (getpid() == 1) {
-        TriggerShutdown("reboot,recovery");
-    } else {
-        property_set("sys.powerctl", "reboot,recovery");
-    }
-    return {};
+    property_set("sys.powerctl", "reboot,recovery");
+    return Success();
 }
 
 template <typename F>
@@ -158,10 +104,10 @@
     }
 }
 
-static Result<void> do_class_start(const BuiltinArguments& args) {
+static Result<Success> 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 {};
+        return Success();
     // Starting a class does not start services which are explicitly disabled.
     // They must  be started individually.
     for (const auto& service : ServiceList::GetInstance()) {
@@ -172,20 +118,13 @@
             }
         }
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_class_start_post_data(const BuiltinArguments& args) {
+static Result<Success> do_class_start_post_data(const BuiltinArguments& args) {
     if (args.context != kInitContext) {
         return Error() << "command 'class_start_post_data' only available in init context";
     }
-    static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
-
-    if (!is_apex_updatable) {
-        // No need to start these on devices that don't support APEX, since they're not
-        // stopped either.
-        return {};
-    }
     for (const auto& service : ServiceList::GetInstance()) {
         if (service->classnames().count(args[1])) {
             if (auto result = service->StartIfPostData(); !result) {
@@ -194,48 +133,43 @@
             }
         }
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_class_stop(const BuiltinArguments& args) {
+static Result<Success> do_class_stop(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Stop);
-    return {};
+    return Success();
 }
 
-static Result<void> do_class_reset(const BuiltinArguments& args) {
+static Result<Success> do_class_reset(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Reset);
-    return {};
+    return Success();
 }
 
-static Result<void> do_class_reset_post_data(const BuiltinArguments& args) {
+static Result<Success> do_class_reset_post_data(const BuiltinArguments& args) {
     if (args.context != kInitContext) {
         return Error() << "command 'class_reset_post_data' only available in init context";
     }
-    static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
-    if (!is_apex_updatable) {
-        // No need to stop these on devices that don't support APEX.
-        return {};
-    }
     ForEachServiceInClass(args[1], &Service::ResetIfPostData);
-    return {};
+    return Success();
 }
 
-static Result<void> do_class_restart(const BuiltinArguments& args) {
+static Result<Success> 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 {};
+        return Success();
     ForEachServiceInClass(args[1], &Service::Restart);
-    return {};
+    return Success();
 }
 
-static Result<void> do_domainname(const BuiltinArguments& args) {
+static Result<Success> do_domainname(const BuiltinArguments& args) {
     if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
         return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_enable(const BuiltinArguments& args) {
+static Result<Success> do_enable(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "Could not find service";
 
@@ -243,36 +177,36 @@
         return Error() << "Could not enable service: " << result.error();
     }
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_exec(const BuiltinArguments& args) {
+static Result<Success> do_exec(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        return Error() << "Could not create exec service: " << service.error();
+        return Error() << "Could not create exec service";
     }
-    if (auto result = (*service)->ExecStart(); !result) {
+    if (auto result = service->ExecStart(); !result) {
         return Error() << "Could not start exec service: " << result.error();
     }
 
-    ServiceList::GetInstance().AddService(std::move(*service));
-    return {};
+    ServiceList::GetInstance().AddService(std::move(service));
+    return Success();
 }
 
-static Result<void> do_exec_background(const BuiltinArguments& args) {
+static Result<Success> do_exec_background(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        return Error() << "Could not create exec background service: " << service.error();
+        return Error() << "Could not create exec background service";
     }
-    if (auto result = (*service)->Start(); !result) {
+    if (auto result = service->Start(); !result) {
         return Error() << "Could not start exec background service: " << result.error();
     }
 
-    ServiceList::GetInstance().AddService(std::move(*service));
-    return {};
+    ServiceList::GetInstance().AddService(std::move(service));
+    return Success();
 }
 
-static Result<void> do_exec_start(const BuiltinArguments& args) {
+static Result<Success> do_exec_start(const BuiltinArguments& args) {
     Service* service = ServiceList::GetInstance().FindService(args[1]);
     if (!service) {
         return Error() << "Service not found";
@@ -282,29 +216,29 @@
         return Error() << "Could not start exec service: " << result.error();
     }
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_export(const BuiltinArguments& args) {
+static Result<Success> do_export(const BuiltinArguments& args) {
     if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
         return ErrnoError() << "setenv() failed";
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_hostname(const BuiltinArguments& args) {
+static Result<Success> do_hostname(const BuiltinArguments& args) {
     if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
         return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_ifup(const BuiltinArguments& args) {
+static Result<Success> do_ifup(const BuiltinArguments& args) {
     struct ifreq ifr;
 
     strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
 
-    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
+    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
     if (s < 0) return ErrnoError() << "opening socket failed";
 
     if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
@@ -317,10 +251,10 @@
         return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
     }
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_insmod(const BuiltinArguments& args) {
+static Result<Success> do_insmod(const BuiltinArguments& args) {
     int flags = 0;
     auto it = args.begin() + 1;
 
@@ -338,89 +272,91 @@
     int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);
     if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed";
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_interface_restart(const BuiltinArguments& args) {
+static Result<Success> do_interface_restart(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
     if (!svc) return Error() << "interface " << args[1] << " not found";
     svc->Restart();
-    return {};
+    return Success();
 }
 
-static Result<void> do_interface_start(const BuiltinArguments& args) {
+static Result<Success> do_interface_start(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
     if (!svc) return Error() << "interface " << args[1] << " not found";
     if (auto result = svc->Start(); !result) {
         return Error() << "Could not start interface: " << result.error();
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_interface_stop(const BuiltinArguments& args) {
+static Result<Success> do_interface_stop(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindInterface(args[1]);
     if (!svc) return Error() << "interface " << args[1] << " not found";
     svc->Stop();
-    return {};
+    return Success();
 }
 
-// mkdir <path> [mode] [owner] [group] [<option> ...]
-static Result<void> do_mkdir(const BuiltinArguments& args) {
-    auto options = ParseMkdir(args.args);
-    if (!options) return options.error();
-    std::string ref_basename;
-    if (options->ref_option == "ref") {
-        ref_basename = fscrypt_key_ref;
-    } else if (options->ref_option == "per_boot_ref") {
-        ref_basename = fscrypt_key_per_boot_ref;
-    } else {
-        return Error() << "Unknown key option: '" << options->ref_option << "'";
+// mkdir <path> [mode] [owner] [group]
+static Result<Success> do_mkdir(const BuiltinArguments& args) {
+    mode_t mode = 0755;
+    if (args.size() >= 3) {
+        mode = std::strtoul(args[2].c_str(), 0, 8);
     }
 
-    struct stat mstat;
-    if (lstat(options->target.c_str(), &mstat) != 0) {
-        if (errno != ENOENT) {
-            return ErrnoError() << "lstat() failed on " << options->target;
-        }
-        if (!make_dir(options->target, options->mode)) {
-            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << options->target;
-        }
-        if (lstat(options->target.c_str(), &mstat) != 0) {
-            return ErrnoError() << "lstat() failed on new " << options->target;
+    if (!make_dir(args[1], mode)) {
+        /* chmod in case the directory already exists */
+        if (errno == EEXIST) {
+            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+                return ErrnoError() << "fchmodat() failed";
+            }
+        } else {
+            return ErrnoError() << "mkdir() failed";
         }
     }
-    if (!S_ISDIR(mstat.st_mode)) {
-        return Error() << "Not a directory on " << options->target;
-    }
-    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options->mode;
-    if ((options->uid != static_cast<uid_t>(-1) && options->uid != mstat.st_uid) ||
-        (options->gid != static_cast<gid_t>(-1) && options->gid != mstat.st_gid)) {
-        if (lchown(options->target.c_str(), options->uid, options->gid) == -1) {
-            return ErrnoError() << "lchown failed on " << options->target;
+
+    if (args.size() >= 4) {
+        auto uid = DecodeUid(args[3]);
+        if (!uid) {
+            return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
         }
-        // chown may have cleared S_ISUID and S_ISGID, chmod again
-        needs_chmod = true;
-    }
-    if (needs_chmod) {
-        if (fchmodat(AT_FDCWD, options->target.c_str(), options->mode, AT_SYMLINK_NOFOLLOW) == -1) {
-            return ErrnoError() << "fchmodat() failed on " << options->target;
+        Result<gid_t> gid = -1;
+
+        if (args.size() == 5) {
+            gid = DecodeUid(args[4]);
+            if (!gid) {
+                return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
+            }
+        }
+
+        if (lchown(args[1].c_str(), *uid, *gid) == -1) {
+            return ErrnoError() << "lchown failed";
+        }
+
+        /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+        if (mode & (S_ISUID | S_ISGID)) {
+            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+                return ErrnoError() << "fchmodat failed";
+            }
         }
     }
+
     if (fscrypt_is_native()) {
-        if (!FscryptSetDirectoryPolicy(ref_basename, options->fscrypt_action, options->target)) {
+        if (fscrypt_set_directory_policy(args[1].c_str())) {
             return reboot_into_recovery(
-                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options->target});
+                {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
         }
     }
-    return {};
+    return Success();
 }
 
 /* umount <path> */
-static Result<void> do_umount(const BuiltinArguments& args) {
+static Result<Success> do_umount(const BuiltinArguments& args) {
     if (umount(args[1].c_str()) < 0) {
         return ErrnoError() << "umount() failed";
     }
-    return {};
+    return Success();
 }
 
 static struct {
@@ -448,7 +384,7 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-static Result<void> do_mount(const BuiltinArguments& args) {
+static Result<Success> do_mount(const BuiltinArguments& args) {
     const char* options = nullptr;
     unsigned flags = 0;
     bool wait = false;
@@ -495,7 +431,7 @@
                         ioctl(loop, LOOP_CLR_FD, 0);
                         return ErrnoError() << "mount() failed";
                     }
-                    return {};
+                    return Success();
                 }
             }
         }
@@ -505,12 +441,12 @@
         if (wait)
             wait_for_file(source, kCommandRetryTimeout);
         if (mount(source, target, system, flags, options) < 0) {
-            return ErrnoErrorIgnoreEnoent() << "mount() failed";
+            return ErrnoError() << "mount() failed";
         }
 
     }
 
-    return {};
+    return Success();
 }
 
 /* Imports .rc files from the specified paths. Default ones are applied if none is given.
@@ -538,6 +474,78 @@
     if (false) DumpState();
 }
 
+/* handle_fstab
+ *
+ *  Read the given fstab file and execute func on it.
+ */
+static Result<int> handle_fstab(const std::string& fstabfile, std::function<int(Fstab*)> func) {
+    /*
+     * Call fs_mgr_[u]mount_all() to [u]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_[u]mount_all() */
+
+        // So we can always see what fs_mgr_[u]mount_all() does.
+        // Only needed if someone explicitly changes the default log level in their init.rc.
+        android::base::ScopedLogSeverity info(android::base::INFO);
+
+        Fstab fstab;
+        ReadFstabFromFile(fstabfile, &fstab);
+
+        int child_ret = func(&fstab);
+
+        _exit(child_ret);
+    } else {
+        return Error() << "fork() failed";
+    }
+}
+
+/* mount_fstab
+ *
+ *  Call fs_mgr_mount_all() to mount the given fstab
+ */
+static Result<int> mount_fstab(const std::string& fstabfile, int mount_mode) {
+    return handle_fstab(fstabfile, [mount_mode](Fstab* fstab) {
+        int ret = fs_mgr_mount_all(fstab, mount_mode);
+        if (ret == -1) {
+            PLOG(ERROR) << "fs_mgr_mount_all returned an error";
+        }
+        return ret;
+    });
+}
+
+/* umount_fstab
+ *
+ *  Call fs_mgr_umount_all() to umount the given fstab
+ */
+static Result<int> umount_fstab(const std::string& fstabfile) {
+    return handle_fstab(fstabfile, [](Fstab* fstab) {
+        int ret = fs_mgr_umount_all(fstab);
+        if (ret != 0) {
+            PLOG(ERROR) << "fs_mgr_umount_all returned " << ret;
+        }
+        return ret;
+    });
+}
+
 /* Queue event based on fs_mgr return code.
  *
  * code: return code of fs_mgr_mount_all
@@ -547,37 +555,23 @@
  *
  * return code is processed based on input code
  */
-static Result<void> queue_fs_event(int code, bool userdata_remount) {
+static Result<Success> queue_fs_event(int code) {
     if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
-        if (userdata_remount) {
-            // FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION should only happen on FDE devices. Since we don't
-            // support userdata remount on FDE devices, this should never been triggered. Time to
-            // panic!
-            LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
-            TriggerShutdown("reboot,requested-userdata-remount-on-fde-device");
-        }
         ActionManager::GetInstance().QueueEventTrigger("encrypt");
-        return {};
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
-        if (userdata_remount) {
-            // FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED should only happen on FDE devices. Since we
-            // don't support userdata remount on FDE devices, this should never been triggered.
-            // Time to panic!
-            LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
-            TriggerShutdown("reboot,requested-userdata-remount-on-fde-device");
-        }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "block");
         ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
-        return {};
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
         property_set("ro.crypto.state", "unencrypted");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-        return {};
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
         property_set("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-        return {};
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         if (android::gsi::IsGsiRunning()) {
@@ -588,8 +582,8 @@
         return reboot_into_recovery(options);
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
-        if (!userdata_remount && !FscryptInstallKeyring()) {
-            return Error() << "FscryptInstallKeyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -597,10 +591,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 {};
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
-        if (!userdata_remount && !FscryptInstallKeyring()) {
-            return Error() << "FscryptInstallKeyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -608,10 +602,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 {};
+        return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-        if (!userdata_remount && !FscryptInstallKeyring()) {
-            return Error() << "FscryptInstallKeyring() failed";
+        if (fscrypt_install_keyring()) {
+            return Error() << "fscrypt_install_keyring() failed";
         }
         property_set("ro.crypto.state", "encrypted");
         property_set("ro.crypto.type", "file");
@@ -619,7 +613,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 {};
+        return Success();
     } else if (code > 0) {
         Error() << "fs_mgr_mount_all() returned unexpected error " << code;
     }
@@ -628,19 +622,17 @@
     return Error() << "Invalid code: " << code;
 }
 
-static int initial_mount_fstab_return_code = -1;
-
 /* mount_all <fstab> [ <path> ]* [--<options>]*
  *
  * This function might request a reboot, in which case it will
  * not return.
  */
-static Result<void> do_mount_all(const BuiltinArguments& args) {
+static Result<Success> 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 auto& fstab_file = args[1];
+    const auto& fstabfile = args[1];
     std::size_t path_arg_end = args.size();
     const char* prop_post_fix = "default";
 
@@ -660,16 +652,13 @@
 
     std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
     android::base::Timer t;
-
-    Fstab fstab;
-    if (!ReadFstabFromFile(fstab_file, &fstab)) {
-        return Error() << "Could not read fstab";
+    auto mount_fstab_return_code = mount_fstab(fstabfile, mount_mode);
+    if (!mount_fstab_return_code) {
+        return Error() << "mount_fstab() failed " << mount_fstab_return_code.error();
     }
-
-    auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
     property_set(prop_name, std::to_string(t.duration().count()));
 
-    if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
+    if (import_rc) {
         /* Paths of .rc files are specified at the 2nd argument and beyond */
         import_late(args.args, 2, path_arg_end);
     }
@@ -677,30 +666,25 @@
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
-        initial_mount_fstab_return_code = mount_fstab_return_code;
-        auto queue_fs_result = queue_fs_event(mount_fstab_return_code, false);
+        auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
         if (!queue_fs_result) {
             return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
         }
     }
 
-    return {};
+    return Success();
 }
 
 /* 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";
+static Result<Success> do_umount_all(const BuiltinArguments& args) {
+    auto umount_fstab_return_code = umount_fstab(args[1]);
+    if (!umount_fstab_return_code) {
+        return Error() << "umount_fstab() failed " << umount_fstab_return_code.error();
     }
-
-    if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
-        return Error() << "umount_fstab() failed " << result;
-    }
-    return {};
+    return Success();
 }
 
-static Result<void> do_swapon_all(const BuiltinArguments& args) {
+static Result<Success> do_swapon_all(const BuiltinArguments& args) {
     Fstab fstab;
     if (!ReadFstabFromFile(args[1], &fstab)) {
         return Error() << "Could not read fstab '" << args[1] << "'";
@@ -710,59 +694,50 @@
         return Error() << "fs_mgr_swapon_all() failed";
     }
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_setprop(const BuiltinArguments& args) {
-    if (StartsWith(args[1], "ctl.")) {
-        return Error()
-               << "Cannot set ctl. properties from init; call the Service functions directly";
-    }
-    if (args[1] == kRestoreconProperty) {
-        return Error() << "Cannot set '" << kRestoreconProperty
-                       << "' from init; use the restorecon builtin directly";
-    }
-
+static Result<Success> do_setprop(const BuiltinArguments& args) {
     property_set(args[1], args[2]);
-    return {};
+    return Success();
 }
 
-static Result<void> do_setrlimit(const BuiltinArguments& args) {
+static Result<Success> do_setrlimit(const BuiltinArguments& args) {
     auto rlimit = ParseRlimit(args.args);
     if (!rlimit) return rlimit.error();
 
     if (setrlimit(rlimit->first, &rlimit->second) == -1) {
         return ErrnoError() << "setrlimit failed";
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_start(const BuiltinArguments& args) {
+static Result<Success> do_start(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     if (auto result = svc->Start(); !result) {
-        return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
+        return Error() << "Could not start service: " << result.error();
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_stop(const BuiltinArguments& args) {
+static Result<Success> do_stop(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Stop();
-    return {};
+    return Success();
 }
 
-static Result<void> do_restart(const BuiltinArguments& args) {
+static Result<Success> do_restart(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
-    return {};
+    return Success();
 }
 
-static Result<void> do_trigger(const BuiltinArguments& args) {
+static Result<Success> do_trigger(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
-    return {};
+    return Success();
 }
 
 static int MakeSymlink(const std::string& target, const std::string& linkpath) {
@@ -783,30 +758,33 @@
     return rc;
 }
 
-static Result<void> do_symlink(const BuiltinArguments& args) {
+static Result<Success> do_symlink(const BuiltinArguments& args) {
     if (MakeSymlink(args[1], args[2]) < 0) {
         // The symlink builtin is often used to create symlinks for older devices to be backwards
         // compatible with new paths, therefore we skip reporting this error.
-        return ErrnoErrorIgnoreEnoent() << "symlink() failed";
+        if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
+            return Success();
+        }
+        return ErrnoError() << "symlink() failed";
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_rm(const BuiltinArguments& args) {
+static Result<Success> do_rm(const BuiltinArguments& args) {
     if (unlink(args[1].c_str()) < 0) {
         return ErrnoError() << "unlink() failed";
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_rmdir(const BuiltinArguments& args) {
+static Result<Success> do_rmdir(const BuiltinArguments& args) {
     if (rmdir(args[1].c_str()) < 0) {
         return ErrnoError() << "rmdir() failed";
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_sysclktz(const BuiltinArguments& args) {
+static Result<Success> do_sysclktz(const BuiltinArguments& args) {
     struct timezone tz = {};
     if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
         return Error() << "Unable to parse mins_west_of_gmt";
@@ -815,10 +793,21 @@
     if (settimeofday(nullptr, &tz) == -1) {
         return ErrnoError() << "settimeofday() failed";
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_verity_update_state(const BuiltinArguments& args) {
+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");
+    }
+    if (!loaded) return Error() << "Could not load verity state";
+
+    return Success();
+}
+
+static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
     int mode;
     if (!fs_mgr_load_verity_state(&mode)) {
         return Error() << "fs_mgr_load_verity_state() failed";
@@ -840,20 +829,19 @@
         property_set("partition." + partition + ".verified", std::to_string(mode));
     }
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_write(const BuiltinArguments& args) {
+static Result<Success> do_write(const BuiltinArguments& args) {
     if (auto result = WriteFile(args[1], args[2]); !result) {
-        return ErrorIgnoreEnoent()
-               << "Unable to write to file '" << args[1] << "': " << result.error();
+        return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
     }
 
-    return {};
+    return Success();
 }
 
-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)));
+static Result<Success> readahead_file(const std::string& filename, bool fully) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
     if (fd == -1) {
         return ErrnoError() << "Error opening file";
     }
@@ -872,10 +860,10 @@
             return ErrnoError() << "Error reading file";
         }
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_readahead(const BuiltinArguments& args) {
+static Result<Success> do_readahead(const BuiltinArguments& args) {
     struct stat sb;
 
     if (stat(args[1].c_str(), &sb)) {
@@ -931,10 +919,10 @@
     } else if (pid < 0) {
         return ErrnoError() << "Fork failed";
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_copy(const BuiltinArguments& args) {
+static Result<Success> do_copy(const BuiltinArguments& args) {
     auto file_contents = ReadFile(args[1]);
     if (!file_contents) {
         return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
@@ -943,10 +931,10 @@
         return Error() << "Could not write to output file '" << args[2] << "': " << result.error();
     }
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_chown(const BuiltinArguments& args) {
+static Result<Success> do_chown(const BuiltinArguments& args) {
     auto uid = DecodeUid(args[1]);
     if (!uid) {
         return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
@@ -964,10 +952,10 @@
     }
 
     if (lchown(path.c_str(), *uid, *gid) == -1) {
-        return ErrnoErrorIgnoreEnoent() << "lchown() failed";
+        return ErrnoError() << "lchown() failed";
     }
 
-    return {};
+    return Success();
 }
 
 static mode_t get_mode(const char *s) {
@@ -983,40 +971,63 @@
     return mode;
 }
 
-static Result<void> do_chmod(const BuiltinArguments& args) {
+static Result<Success> do_chmod(const BuiltinArguments& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
-        return ErrnoErrorIgnoreEnoent() << "fchmodat() failed";
+        return ErrnoError() << "fchmodat() failed";
     }
-    return {};
+    return Success();
 }
 
-static Result<void> do_restorecon(const BuiltinArguments& args) {
-    auto restorecon_info = ParseRestorecon(args.args);
-    if (!restorecon_info) {
-        return restorecon_info.error();
-    }
-
-    const auto& [flag, paths] = *restorecon_info;
-
+static Result<Success> do_restorecon(const BuiltinArguments& args) {
     int ret = 0;
-    for (const auto& path : paths) {
-        if (selinux_android_restorecon(path.c_str(), flag) < 0) {
-            ret = errno;
+
+    struct flag_type {const char* name; int value;};
+    static const flag_type flags[] = {
+        {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
+        {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
+        {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
+        {0, 0}
+    };
+
+    int flag = 0;
+
+    bool in_flags = true;
+    for (size_t i = 1; i < args.size(); ++i) {
+        if (android::base::StartsWith(args[i], "--")) {
+            if (!in_flags) {
+                return Error() << "flags must precede paths";
+            }
+            bool found = false;
+            for (size_t j = 0; flags[j].name; ++j) {
+                if (args[i] == flags[j].name) {
+                    flag |= flags[j].value;
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                return Error() << "bad flag " << args[i];
+            }
+        } else {
+            in_flags = false;
+            if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
+                ret = errno;
+            }
         }
     }
 
-    if (ret) return ErrnoErrorIgnoreEnoent() << "selinux_android_restorecon() failed";
-    return {};
+    if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
+    return Success();
 }
 
-static Result<void> do_restorecon_recursive(const BuiltinArguments& args) {
+static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
     std::vector<std::string> non_const_args(args.args);
     non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
     return do_restorecon({std::move(non_const_args), args.context});
 }
 
-static Result<void> do_loglevel(const BuiltinArguments& args) {
+static Result<Success> do_loglevel(const BuiltinArguments& args) {
     // TODO: support names instead/as well?
     int log_level = -1;
     android::base::ParseInt(args[1], &log_level);
@@ -1034,33 +1045,20 @@
             return Error() << "invalid log level " << log_level;
     }
     android::base::SetMinimumLogSeverity(severity);
-    return {};
+    return Success();
 }
 
-static Result<void> do_load_persist_props(const BuiltinArguments& args) {
-    // Devices with FDE have load_persist_props called twice; the first time when the temporary
-    // /data partition is mounted and then again once /data is truly mounted.  We do not want to
-    // read persistent properties from the temporary /data partition or mark persistent properties
-    // as having been loaded during the first call, so we return in that case.
-    std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
-    std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
-    if (crypto_state == "encrypted" && crypto_type == "block") {
-        static size_t num_calls = 0;
-        if (++num_calls == 1) return {};
-    }
-
-    SendLoadPersistentPropertiesMessage();
-
-    start_waiting_for_property("ro.persistent_properties.ready", "true");
-    return {};
+static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
+    load_persist_props();
+    return Success();
 }
 
-static Result<void> do_load_system_props(const BuiltinArguments& args) {
+static Result<Success> do_load_system_props(const BuiltinArguments& args) {
     LOG(INFO) << "deprecated action `load_system_props` called.";
-    return {};
+    return Success();
 }
 
-static Result<void> do_wait(const BuiltinArguments& args) {
+static Result<Success> do_wait(const BuiltinArguments& args) {
     auto timeout = kCommandRetryTimeout;
     if (args.size() == 3) {
         int timeout_int;
@@ -1074,10 +1072,10 @@
         return Error() << "wait_for_file() failed";
     }
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_wait_for_prop(const BuiltinArguments& args) {
+static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
     const char* name = args[1].c_str();
     const char* value = args[2].c_str();
     size_t value_len = strlen(value);
@@ -1091,94 +1089,71 @@
     if (!start_waiting_for_property(name, value)) {
         return Error() << "already waiting for a property";
     }
-    return {};
+    return Success();
 }
 
 static bool is_file_crypto() {
     return android::base::GetProperty("ro.crypto.type", "") == "file";
 }
 
-static Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& args,
-                                              std::function<void(const std::string&)> function) {
-    auto service = Service::MakeTemporaryOneshotService(args);
+static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
+                                               const BuiltinArguments& args) {
+    auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        function("MakeTemporaryOneshotService failed: " + service.error().message());
+        return Error() << "Could not create exec service";
     }
-    (*service)->AddReapCallback([function](const siginfo_t& siginfo) {
+    service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
-            function(StringPrintf("Exec service failed, status %d", siginfo.si_status));
+            // TODO (b/122850122): support this in gsi
+            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+                LOG(ERROR) << "Rebooting into recovery, reason: " << 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;
+            }
         }
     });
-    if (auto result = (*service)->ExecStart(); !result) {
-        function("ExecStart failed: " + result.error().message());
+    if (auto result = service->ExecStart(); !result) {
+        return Error() << "Could not start exec service: " << result.error();
     }
-    ServiceList::GetInstance().AddService(std::move(*service));
-    return {};
+    ServiceList::GetInstance().AddService(std::move(service));
+    return Success();
 }
 
-static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
-    auto reboot_reason = vdc_arg + "_failed";
-
-    auto reboot = [reboot_reason](const std::string& message) {
-        // TODO (b/122850122): support this in gsi
-        if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
-            LOG(ERROR) << message << ": Rebooting into recovery, reason: " << 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;
-        }
-    };
-
-    std::vector<std::string> args = {"exec", "/system/bin/vdc", "--wait", "cryptfs", vdc_arg};
-    return ExecWithFunctionOnFailure(args, reboot);
-}
-
-static Result<void> do_remount_userdata(const BuiltinArguments& args) {
-    if (initial_mount_fstab_return_code == -1) {
-        return Error() << "Calling remount_userdata too early";
-    }
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
-        // TODO(b/135984674): should we reboot here?
-        return Error() << "Failed to read fstab";
-    }
-    // TODO(b/135984674): check that fstab contains /data.
-    if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
-        TriggerShutdown("reboot,mount-userdata-failed");
-    }
-    if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result) {
-        return Error() << "queue_fs_event() failed: " << result.error();
-    }
-    return {};
-}
-
-static Result<void> do_installkey(const BuiltinArguments& args) {
-    if (!is_file_crypto()) return {};
+static Result<Success> do_installkey(const BuiltinArguments& args) {
+    if (!is_file_crypto()) return Success();
 
     auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;
     if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
         return ErrnoError() << "Failed to create " << unencrypted_dir;
     }
-    return ExecVdcRebootOnFailure("enablefilecrypto");
+    return ExecWithRebootOnFailure(
+        "enablefilecrypto_failed",
+        {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto"}, args.context});
 }
 
-static Result<void> do_init_user0(const BuiltinArguments& args) {
-    return ExecVdcRebootOnFailure("init_user0");
+static Result<Success> 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) {
+static Result<Success> do_mark_post_data(const BuiltinArguments& args) {
     ServiceList::GetInstance().MarkPostData();
 
-    return {};
+    return Success();
 }
 
-static Result<void> do_parse_apex_configs(const BuiltinArguments& args) {
+static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) {
     glob_t glob_result;
-    static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
+    // @ is added to filter out the later paths, which are bind mounts of the places
+    // where the APEXes are really mounted at. Otherwise, we will parse the
+    // same file twice.
+    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);
@@ -1187,15 +1162,7 @@
     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);
+        configs.emplace_back(glob_result.gl_pathv[i]);
     }
     globfree(&glob_result);
 
@@ -1209,25 +1176,25 @@
     }
     ServiceList::GetInstance().MarkServicesUpdate();
     if (success) {
-        return {};
+        return Success();
     } else {
         return Error() << "Could not parse apex configs";
     }
 }
 
-static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
+static Result<Success> do_enter_default_mount_ns(const BuiltinArguments& args) {
     if (SwitchToDefaultMountNamespace()) {
-        return {};
+        return Success();
     } else {
         return Error() << "Failed to enter into default mount namespace";
     }
 }
 
 // Builtin-function-map start
-const BuiltinFunctionMap& GetBuiltinFunctionMap() {
+const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
-    static const BuiltinFunctionMap builtin_functions = {
+    static const Map builtin_functions = {
         {"bootchart",               {1,     1,    {false,  do_bootchart}}},
         {"chmod",                   {2,     2,    {true,   do_chmod}}},
         {"chown",                   {2,     3,    {true,   do_chown}}},
@@ -1256,7 +1223,7 @@
         {"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,     6,    {true,   do_mkdir}}},
+        {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
         // TODO: Do mount operations in vendor_init.
         // mount_all is currently too complex to run in vendor_init as it queues action triggers,
         // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
@@ -1267,7 +1234,6 @@
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"umount_all",              {1,     1,    {false,  do_umount_all}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
-        {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
         {"restart",                 {1,     1,    {false,  do_restart}}},
         {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
         {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
@@ -1282,6 +1248,7 @@
         {"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 f0ff1eb..814b2d5 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_BUILTINS_H
+#define _INIT_BUILTINS_H
 
 #include <functional>
 #include <map>
@@ -28,18 +29,18 @@
 namespace android {
 namespace init {
 
-using BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;
+using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
 
-struct BuiltinFunctionMapValue {
-    bool run_in_subcontext;
-    BuiltinFunction function;
+using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
+class BuiltinFunctionMap : public KeywordFunctionMap {
+  public:
+    BuiltinFunctionMap() {}
+
+  private:
+    const Map& map() const override;
 };
 
-using BuiltinFunctionMap = KeywordMap<BuiltinFunctionMapValue>;
-
-const BuiltinFunctionMap& GetBuiltinFunctionMap();
-
-extern std::vector<std::string> late_import_paths;
-
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
deleted file mode 100644
index 9d23921..0000000
--- a/init/check_builtins.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * 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.
- */
-
-// Note that these check functions cannot check expanded arguments from properties, since they will
-// not know what those properties would be at runtime.  They will be passed an empty string in the
-// situation that the input line had a property expansion without a default value, since an empty
-// string is otherwise an impossible value.  They should therefore disregard checking empty
-// arguments.
-
-#include "check_builtins.h"
-
-#include <sys/time.h>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-
-#include "builtin_arguments.h"
-#include "interface_utils.h"
-#include "rlimit_parser.h"
-#include "service.h"
-#include "util.h"
-
-using android::base::ParseInt;
-using android::base::StartsWith;
-
-#define ReturnIfAnyArgsEmpty()     \
-    for (const auto& arg : args) { \
-        if (arg.empty()) {         \
-            return {};             \
-        }                          \
-    }
-
-namespace android {
-namespace init {
-
-Result<void> check_chown(const BuiltinArguments& args) {
-    if (!args[1].empty()) {
-        auto uid = DecodeUid(args[1]);
-        if (!uid) {
-            return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
-        }
-    }
-
-    // GID is optional and pushes the index of path out by one if specified.
-    if (args.size() == 4 && !args[2].empty()) {
-        auto gid = DecodeUid(args[2]);
-        if (!gid) {
-            return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
-        }
-    }
-
-    return {};
-}
-
-Result<void> check_exec(const BuiltinArguments& args) {
-    ReturnIfAnyArgsEmpty();
-
-    auto result = Service::MakeTemporaryOneshotService(args.args);
-    if (!result) {
-        return result.error();
-    }
-
-    return {};
-}
-
-Result<void> check_exec_background(const BuiltinArguments& args) {
-    return check_exec(std::move(args));
-}
-
-Result<void> check_exec_reboot_on_failure(const BuiltinArguments& args) {
-    BuiltinArguments remaining_args(args.context);
-
-    remaining_args.args = std::vector<std::string>(args.begin() + 1, args.end());
-    remaining_args.args[0] = args[0];
-
-    return check_exec(remaining_args);
-}
-
-Result<void> check_interface_restart(const BuiltinArguments& args) {
-    if (auto result = IsKnownInterface(args[1]); !result) {
-        return result.error();
-    }
-    return {};
-}
-
-Result<void> check_interface_start(const BuiltinArguments& args) {
-    return check_interface_restart(std::move(args));
-}
-
-Result<void> check_interface_stop(const BuiltinArguments& args) {
-    return check_interface_restart(std::move(args));
-}
-
-Result<void> check_load_system_props(const BuiltinArguments& args) {
-    return Error() << "'load_system_props' is deprecated";
-}
-
-Result<void> check_loglevel(const BuiltinArguments& args) {
-    ReturnIfAnyArgsEmpty();
-
-    int log_level = -1;
-    ParseInt(args[1], &log_level);
-    if (log_level < 0 || log_level > 7) {
-        return Error() << "loglevel must be in the range of 0-7";
-    }
-    return {};
-}
-
-Result<void> check_mkdir(const BuiltinArguments& args) {
-    auto options = ParseMkdir(args.args);
-    if (!options) {
-        return options.error();
-    }
-    return {};
-}
-
-Result<void> check_restorecon(const BuiltinArguments& args) {
-    ReturnIfAnyArgsEmpty();
-
-    auto restorecon_info = ParseRestorecon(args.args);
-    if (!restorecon_info) {
-        return restorecon_info.error();
-    }
-
-    return {};
-}
-
-Result<void> check_restorecon_recursive(const BuiltinArguments& args) {
-    return check_restorecon(std::move(args));
-}
-
-Result<void> check_setprop(const BuiltinArguments& args) {
-    const std::string& name = args[1];
-    if (name.empty()) {
-        return {};
-    }
-    const std::string& value = args[2];
-
-    if (!IsLegalPropertyName(name)) {
-        return Error() << "'" << name << "' is not a legal property name";
-    }
-
-    if (!value.empty()) {
-        if (auto result = IsLegalPropertyValue(name, value); !result) {
-            return result.error();
-        }
-    }
-
-    if (StartsWith(name, "ctl.")) {
-        return Error()
-               << "Do not set ctl. properties from init; call the Service functions directly";
-    }
-
-    static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
-    if (name == kRestoreconProperty) {
-        return Error() << "Do not set '" << kRestoreconProperty
-                       << "' from init; use the restorecon builtin directly";
-    }
-
-    return {};
-}
-
-Result<void> check_setrlimit(const BuiltinArguments& args) {
-    ReturnIfAnyArgsEmpty();
-
-    auto rlimit = ParseRlimit(args.args);
-    if (!rlimit) return rlimit.error();
-    return {};
-}
-
-Result<void> check_sysclktz(const BuiltinArguments& args) {
-    ReturnIfAnyArgsEmpty();
-
-    struct timezone tz = {};
-    if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
-        return Error() << "Unable to parse mins_west_of_gmt";
-    }
-    return {};
-}
-
-Result<void> check_wait(const BuiltinArguments& args) {
-    if (args.size() == 3 && !args[2].empty()) {
-        int timeout_int;
-        if (!android::base::ParseInt(args[2], &timeout_int)) {
-            return Error() << "failed to parse timeout";
-        }
-    }
-    return {};
-}
-
-Result<void> check_wait_for_prop(const BuiltinArguments& args) {
-    return check_setprop(std::move(args));
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/check_builtins.h b/init/check_builtins.h
deleted file mode 100644
index fb34556..0000000
--- a/init/check_builtins.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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 "builtin_arguments.h"
-#include "result.h"
-
-namespace android {
-namespace init {
-
-Result<void> check_chown(const BuiltinArguments& args);
-Result<void> check_exec(const BuiltinArguments& args);
-Result<void> check_exec_background(const BuiltinArguments& args);
-Result<void> check_exec_reboot_on_failure(const BuiltinArguments& args);
-Result<void> check_interface_restart(const BuiltinArguments& args);
-Result<void> check_interface_start(const BuiltinArguments& args);
-Result<void> check_interface_stop(const BuiltinArguments& args);
-Result<void> check_load_system_props(const BuiltinArguments& args);
-Result<void> check_loglevel(const BuiltinArguments& args);
-Result<void> check_mkdir(const BuiltinArguments& args);
-Result<void> check_restorecon(const BuiltinArguments& args);
-Result<void> check_restorecon_recursive(const BuiltinArguments& args);
-Result<void> check_setprop(const BuiltinArguments& args);
-Result<void> check_setrlimit(const BuiltinArguments& args);
-Result<void> check_sysclktz(const BuiltinArguments& args);
-Result<void> check_wait(const BuiltinArguments& args);
-Result<void> check_wait_for_prop(const BuiltinArguments& args);
-
-}  // namespace init
-}  // namespace android
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
new file mode 100644
index 0000000..6265687
--- /dev/null
+++ b/init/descriptors.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "descriptors.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/sockets.h>
+
+#include "util.h"
+
+namespace android {
+namespace init {
+
+DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
+                               gid_t gid, int perm, const std::string& context)
+        : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
+}
+
+DescriptorInfo::~DescriptorInfo() {
+}
+
+std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
+  return os << "  descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
+}
+
+bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
+  return name_ == other.name_ && type_ == other.type_ && key() == other.key();
+}
+
+void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
+  // Create
+  const std::string& contextStr = context_.empty() ? globalContext : context_;
+  int fd = Create(contextStr);
+  if (fd < 0) return;
+
+  // Publish
+  std::string publishedName = key() + name_;
+  std::for_each(publishedName.begin(), publishedName.end(),
+                [] (char& c) { c = isalnum(c) ? c : '_'; });
+
+  std::string val = std::to_string(fd);
+  setenv(publishedName.c_str(), val.c_str(), 1);
+
+  // make sure we don't close on exec
+  fcntl(fd, F_SETFD, 0);
+}
+
+void DescriptorInfo::Clean() const {
+}
+
+SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+                       gid_t gid, int perm, const std::string& context)
+        : DescriptorInfo(name, type, uid, gid, perm, context) {
+}
+
+void SocketInfo::Clean() const {
+    std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
+    unlink(path.c_str());
+}
+
+int SocketInfo::Create(const std::string& context) const {
+    auto types = android::base::Split(type(), "+");
+    int flags =
+        ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
+    bool passcred = types.size() > 1 && types[1] == "passcred";
+    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
+}
+
+const std::string SocketInfo::key() const {
+  return ANDROID_SOCKET_ENV_PREFIX;
+}
+
+FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
+                   gid_t gid, int perm, const std::string& context)
+        // defaults OK for uid,..., they are ignored for this class.
+        : DescriptorInfo(name, type, uid, gid, perm, context) {
+}
+
+int FileInfo::Create(const std::string&) const {
+  int flags = (type() == "r") ? O_RDONLY :
+              (type() == "w") ? O_WRONLY :
+                                O_RDWR;
+
+  // Make sure we do not block on open (eg: devices can chose to block on
+  // carrier detect).  Our intention is never to delay launch of a service
+  // for such a condition.  The service can perform its own blocking on
+  // carrier detect.
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name().c_str(),
+                                                      flags | O_NONBLOCK)));
+
+  if (fd < 0) {
+    PLOG(ERROR) << "Failed to open file '" << name().c_str() << "'";
+    return -1;
+  }
+
+  // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
+  fcntl(fd, F_SETFL, flags);
+
+  LOG(INFO) << "Opened file '" << name().c_str() << "'"
+            << ", flags " << std::oct << flags << std::dec;
+
+  return fd.release();
+}
+
+const std::string FileInfo::key() const {
+  return ANDROID_FILE_ENV_PREFIX;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/descriptors.h b/init/descriptors.h
new file mode 100644
index 0000000..3bdddfe
--- /dev/null
+++ b/init/descriptors.h
@@ -0,0 +1,84 @@
+/*
+ * 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 _INIT_DESCRIPTORS_H
+#define _INIT_DESCRIPTORS_H
+
+#include <sys/types.h>
+
+#include <string>
+
+namespace android {
+namespace init {
+
+class DescriptorInfo {
+ public:
+  DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
+                 gid_t gid, int perm, const std::string& context);
+  virtual ~DescriptorInfo();
+
+  friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
+  bool operator==(const DescriptorInfo& other) const;
+
+  void CreateAndPublish(const std::string& globalContext) const;
+  virtual void Clean() const;
+
+ protected:
+  const std::string& name() const { return name_; }
+  const std::string& type() const { return type_; }
+  uid_t uid() const { return uid_; }
+  gid_t gid() const { return gid_; }
+  int perm() const { return perm_; }
+  const std::string& context() const { return context_; }
+
+ private:
+  std::string name_;
+  std::string type_;
+  uid_t uid_;
+  gid_t gid_;
+  int perm_;
+  std::string context_;
+
+  virtual int Create(const std::string& globalContext) const = 0;
+  virtual const std::string key() const = 0;
+};
+
+std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
+
+class SocketInfo : public DescriptorInfo {
+ public:
+  SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+             gid_t gid, int perm, const std::string& context);
+  void Clean() const override;
+ private:
+  virtual int Create(const std::string& context) const override;
+  virtual const std::string key() const override;
+};
+
+class FileInfo : public DescriptorInfo {
+ public:
+  FileInfo(const std::string& name, const std::string& type, uid_t uid,
+           gid_t gid, int perm, const std::string& context);
+ private:
+  virtual int Create(const std::string& context) const override;
+  virtual const std::string key() const override;
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/devices.cpp b/init/devices.cpp
index 9fbec64..159c75e 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -22,6 +22,7 @@
 #include <unistd.h>
 
 #include <chrono>
+#include <map>
 #include <memory>
 #include <string>
 #include <thread>
@@ -35,9 +36,13 @@
 #include <selinux/android.h>
 #include <selinux/selinux.h>
 
-#include "selabel.h"
+#include "selinux.h"
 #include "util.h"
 
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd; it will expose init's globals"
+#endif
+
 using namespace std::chrono_literals;
 
 using android::base::Basename;
@@ -110,16 +115,24 @@
 // 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 FindDmDevice(const std::string& path, std::string* name, std::string* uuid) {
+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
 
-    if (!ReadFileToString("/sys" + path + "/dm/name", name)) {
-        return false;
+    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;
+        }
     }
-    ReadFileToString("/sys" + path + "/dm/uuid", uuid);
-
-    *name = android::base::Trim(*name);
-    *uuid = android::base::Trim(*uuid);
+    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;
 }
 
@@ -316,7 +329,6 @@
     std::string device;
     std::string type;
     std::string partition;
-    std::string uuid;
 
     if (FindPlatformDevice(uevent.path, &device)) {
         // Skip /devices/platform or /devices/ if present
@@ -334,12 +346,8 @@
         type = "pci";
     } else if (FindVbdDevicePrefix(uevent.path, &device)) {
         type = "vbd";
-    } else if (FindDmDevice(uevent.path, &partition, &uuid)) {
-        std::vector<std::string> symlinks = {"/dev/block/mapper/" + partition};
-        if (!uuid.empty()) {
-            symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
-        }
-        return symlinks;
+    } else if (FindDmDevicePartition(uevent.path, &partition)) {
+        return {"/dev/block/mapper/" + partition};
     } else {
         return {};
     }
@@ -375,41 +383,10 @@
     return links;
 }
 
-static void RemoveDeviceMapperLinks(const std::string& devpath) {
-    std::vector<std::string> dirs = {
-            "/dev/block/mapper",
-            "/dev/block/mapper/by-uuid",
-    };
-    for (const auto& dir : dirs) {
-        if (access(dir.c_str(), F_OK) != 0) continue;
-
-        std::unique_ptr<DIR, decltype(&closedir)> dh(opendir(dir.c_str()), closedir);
-        if (!dh) {
-            PLOG(ERROR) << "Failed to open directory " << dir;
-            continue;
-        }
-
-        struct dirent* dp;
-        std::string link_path;
-        while ((dp = readdir(dh.get())) != nullptr) {
-            if (dp->d_type != DT_LNK) continue;
-
-            auto path = dir + "/" + dp->d_name;
-            if (Readlink(path, &link_path) && link_path == devpath) {
-                unlink(path.c_str());
-            }
-        }
-    }
-}
-
 void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
                                  int major, int minor, const std::vector<std::string>& links) const {
     if (action == "add") {
         MakeDevice(devpath, block, major, minor, links);
-    }
-
-    // We don't have full device-mapper information until a change event is fired.
-    if (action == "add" || (action == "change" && StartsWith(devpath, "/dev/block/dm-"))) {
         for (const auto& link : links) {
             if (!mkdir_recursive(Dirname(link), 0755)) {
                 PLOG(ERROR) << "Failed to create directory " << Dirname(link);
@@ -428,9 +405,6 @@
     }
 
     if (action == "remove") {
-        if (StartsWith(devpath, "/dev/block/dm-")) {
-            RemoveDeviceMapperLinks(devpath);
-        }
         for (const auto& link : links) {
             std::string link_path;
             if (Readlink(link, &link_path) && link_path == devpath) {
@@ -441,23 +415,6 @@
     }
 }
 
-void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
-    if (uevent.device_name == "ashmem") {
-        static const std::string boot_id_path = "/proc/sys/kernel/random/boot_id";
-        std::string boot_id;
-        if (!ReadFileToString(boot_id_path, &boot_id)) {
-            PLOG(ERROR) << "Cannot duplicate ashmem device node. Failed to read " << boot_id_path;
-            return;
-        };
-        boot_id = Trim(boot_id);
-
-        Uevent dup_ashmem_uevent = uevent;
-        dup_ashmem_uevent.device_name += boot_id;
-        dup_ashmem_uevent.path += boot_id;
-        HandleUevent(dup_ashmem_uevent);
-    }
-}
-
 void DeviceHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
         FixupSysPermissions(uevent.path, uevent.subsystem);
@@ -502,10 +459,6 @@
     mkdir_recursive(Dirname(devpath), 0755);
 
     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
-
-    // Duplicate /dev/ashmem device and name it /dev/ashmem<boot_id>.
-    // TODO(b/111903542): remove once all users of /dev/ashmem are migrated to libcutils API.
-    HandleAshmemUevent(uevent);
 }
 
 void DeviceHandler::ColdbootDone() {
diff --git a/init/devices.h b/init/devices.h
index 05d64da..9d39eaa 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -84,9 +84,9 @@
     };
 
     Subsystem() {}
-    Subsystem(std::string name) : name_(std::move(name)) {}
-    Subsystem(std::string name, DevnameSource source, std::string dir_name)
-        : name_(std::move(name)), devname_source_(source), dir_name_(std::move(dir_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
@@ -130,7 +130,6 @@
     void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major,
                       int minor, const std::vector<std::string>& links) const;
     void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
-    void HandleAshmemUevent(const Uevent& uevent);
 
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index c408bc1..3e7c1a8 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -30,7 +30,7 @@
 class DeviceHandlerTester {
   public:
     void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
-                         const std::vector<std::string>& expected_links) {
+                         const std::vector<std::string> expected_links) {
         TemporaryDir fake_sys_root;
         device_handler_.sysfs_mount_point_ = fake_sys_root.path;
 
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 17d63fa..94dd553 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -28,17 +28,17 @@
 
 Epoll::Epoll() {}
 
-Result<void> Epoll::Open() {
-    if (epoll_fd_ >= 0) return {};
+Result<Success> Epoll::Open() {
+    if (epoll_fd_ >= 0) return Success();
     epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
 
     if (epoll_fd_ == -1) {
         return ErrnoError() << "epoll_create1 failed";
     }
-    return {};
+    return Success();
 }
 
-Result<void> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
+Result<Success> Epoll::RegisterHandler(int fd, std::function<void()> handler, uint32_t events) {
     if (!events) {
         return Error() << "Must specify events";
     }
@@ -52,41 +52,36 @@
     // 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";
+        Result<Success> result = ErrnoError() << "epoll_ctl failed to add fd";
         epoll_handlers_.erase(fd);
         return result;
     }
-    return {};
+    return Success();
 }
 
-Result<void> Epoll::UnregisterHandler(int fd) {
+Result<Success> Epoll::UnregisterHandler(int fd) {
     if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1) {
         return ErrnoError() << "epoll_ctl failed to remove fd";
     }
     if (epoll_handlers_.erase(fd) != 1) {
         return Error() << "Attempting to remove epoll handler for FD without an existing handler";
     }
-    return {};
+    return Success();
 }
 
-Result<std::vector<std::function<void()>*>> Epoll::Wait(
-        std::optional<std::chrono::milliseconds> timeout) {
+Result<Success> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
     int timeout_ms = -1;
     if (timeout && timeout->count() < INT_MAX) {
         timeout_ms = timeout->count();
     }
-    const auto max_events = epoll_handlers_.size();
-    epoll_event ev[max_events];
-    auto num_events = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, ev, max_events, timeout_ms));
-    if (num_events == -1) {
+    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));
     }
-    std::vector<std::function<void()>*> pending_functions;
-    for (int i = 0; i < num_events; ++i) {
-        pending_functions.emplace_back(reinterpret_cast<std::function<void()>*>(ev[i].data.ptr));
-    }
-
-    return pending_functions;
+    return Success();
 }
 
 }  // namespace init
diff --git a/init/epoll.h b/init/epoll.h
index c32a661..9789bef 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_EPOLL_H
+#define _INIT_EPOLL_H
 
 #include <stdint.h>
 #include <sys/epoll.h>
@@ -23,7 +24,6 @@
 #include <functional>
 #include <map>
 #include <optional>
-#include <vector>
 
 #include <android-base/unique_fd.h>
 
@@ -36,11 +36,11 @@
   public:
     Epoll();
 
-    Result<void> Open();
-    Result<void> RegisterHandler(int fd, std::function<void()> handler, uint32_t events = EPOLLIN);
-    Result<void> UnregisterHandler(int fd);
-    Result<std::vector<std::function<void()>*>> Wait(
-            std::optional<std::chrono::milliseconds> timeout);
+    Result<Success> Open();
+    Result<Success> RegisterHandler(int fd, std::function<void()> handler,
+                                    uint32_t events = EPOLLIN);
+    Result<Success> UnregisterHandler(int fd);
+    Result<Success> Wait(std::optional<std::chrono::milliseconds> timeout);
 
   private:
     android::base::unique_fd epoll_fd_;
@@ -49,3 +49,5 @@
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 1dce2d5..740e82c 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -17,10 +17,6 @@
 #include "firmware_handler.h"
 
 #include <fcntl.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
 #include <sys/sendfile.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -30,29 +26,25 @@
 #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>
 
-using android::base::ReadFdToString;
-using android::base::Socketpair;
-using android::base::Split;
 using android::base::Timer;
-using android::base::Trim;
 using android::base::unique_fd;
 using android::base::WriteFully;
 
 namespace android {
 namespace init {
 
-static void LoadFirmware(const std::string& firmware, const std::string& root, int fw_fd,
-                         size_t fw_size, int loading_fd, int data_fd) {
+static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
+                         int loading_fd, int data_fd) {
     // Start transfer.
     WriteFully(loading_fd, "1", 1);
 
     // Copy the firmware.
     int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
     if (rc == -1) {
-        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << firmware << "' }";
+        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
+                    << "' }";
     }
 
     // Tell the firmware whether to abort or commit.
@@ -64,165 +56,39 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
-FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
-                                 std::vector<ExternalFirmwareHandler> external_firmware_handlers)
-    : firmware_directories_(std::move(firmware_directories)),
-      external_firmware_handlers_(std::move(external_firmware_handlers)) {}
+FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories)
+    : firmware_directories_(std::move(firmware_directories)) {}
 
-Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handler, uid_t uid,
-                                                        const Uevent& uevent) const {
-    unique_fd child_stdout;
-    unique_fd parent_stdout;
-    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) {
-        return ErrnoError() << "Socketpair() for stdout failed";
-    }
+void FirmwareHandler::ProcessFirmwareEvent(const Uevent& uevent) {
+    int booting = IsBooting();
 
-    unique_fd child_stderr;
-    unique_fd parent_stderr;
-    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stderr, &parent_stderr)) {
-        return ErrnoError() << "Socketpair() for stderr failed";
-    }
-
-    signal(SIGCHLD, SIG_DFL);
-
-    auto pid = fork();
-    if (pid < 0) {
-        return ErrnoError() << "fork() failed";
-    }
-
-    if (pid == 0) {
-        setenv("FIRMWARE", uevent.firmware.c_str(), 1);
-        setenv("DEVPATH", uevent.path.c_str(), 1);
-        parent_stdout.reset();
-        parent_stderr.reset();
-        close(STDOUT_FILENO);
-        close(STDERR_FILENO);
-        dup2(child_stdout.get(), STDOUT_FILENO);
-        dup2(child_stderr.get(), STDERR_FILENO);
-
-        auto args = Split(handler, " ");
-        std::vector<char*> c_args;
-        for (auto& arg : args) {
-            c_args.emplace_back(arg.data());
-        }
-        c_args.emplace_back(nullptr);
-
-        if (setuid(uid) != 0) {
-            fprintf(stderr, "setuid() failed: %s", strerror(errno));
-            _exit(EXIT_FAILURE);
-        }
-
-        execv(c_args[0], c_args.data());
-        fprintf(stderr, "exec() failed: %s", strerror(errno));
-        _exit(EXIT_FAILURE);
-    }
-
-    child_stdout.reset();
-    child_stderr.reset();
-
-    int status;
-    pid_t waited_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
-    if (waited_pid == -1) {
-        return ErrnoError() << "waitpid() failed";
-    }
-
-    std::string stdout_content;
-    if (!ReadFdToString(parent_stdout.get(), &stdout_content)) {
-        return ErrnoError() << "ReadFdToString() for stdout failed";
-    }
-
-    std::string stderr_content;
-    if (ReadFdToString(parent_stderr.get(), &stderr_content)) {
-        auto messages = Split(stderr_content, "\n");
-        for (const auto& message : messages) {
-            if (!message.empty()) {
-                LOG(ERROR) << "External Firmware Handler: " << message;
-            }
-        }
-    } else {
-        LOG(ERROR) << "ReadFdToString() for stderr failed";
-    }
-
-    if (WIFEXITED(status)) {
-        if (WEXITSTATUS(status) == EXIT_SUCCESS) {
-            return Trim(stdout_content);
-        } else {
-            return Error() << "exited with status " << WEXITSTATUS(status);
-        }
-    } else if (WIFSIGNALED(status)) {
-        return Error() << "killed by signal " << WTERMSIG(status);
-    }
-
-    return Error() << "unexpected exit status " << status;
-}
-
-std::string FirmwareHandler::GetFirmwarePath(const Uevent& uevent) const {
-    for (const auto& external_handler : external_firmware_handlers_) {
-        if (external_handler.devpath == uevent.path) {
-            LOG(INFO) << "Launching external firmware handler '" << external_handler.handler_path
-                      << "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
-                      << "'";
-
-            auto result =
-                    RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent);
-            if (!result) {
-                LOG(ERROR) << "Using default firmware; External firmware handler failed: "
-                           << result.error();
-                return uevent.firmware;
-            }
-            if (result->find("..") != std::string::npos) {
-                LOG(ERROR) << "Using default firmware; External firmware handler provided an "
-                              "invalid path, '"
-                           << *result << "'";
-                return uevent.firmware;
-            }
-            LOG(INFO) << "Loading firmware '" << *result << "' in place of '" << uevent.firmware
-                      << "'";
-            return *result;
-        }
-    }
     LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
-    return uevent.firmware;
-}
 
-void FirmwareHandler::ProcessFirmwareEvent(const std::string& root,
-                                           const std::string& firmware) const {
+    std::string root = "/sys" + uevent.path;
     std::string loading = root + "/loading";
     std::string data = root + "/data";
 
     unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
     if (loading_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware loading fd for " << firmware;
+        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
         return;
     }
 
     unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
     if (data_fd == -1) {
-        PLOG(ERROR) << "couldn't open firmware data fd for " << firmware;
+        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
         return;
     }
 
-    std::vector<std::string> attempted_paths_and_errors;
-
-    int booting = IsBooting();
 try_loading_again:
-    attempted_paths_and_errors.clear();
     for (const auto& firmware_directory : firmware_directories_) {
-        std::string file = firmware_directory + firmware;
+        std::string file = firmware_directory + uevent.firmware;
         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
-        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;
+        if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+            LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+            return;
         }
-        LoadFirmware(firmware, root, fw_fd, sb.st_size, loading_fd, data_fd);
-        return;
     }
 
     if (booting) {
@@ -233,10 +99,7 @@
         goto try_loading_again;
     }
 
-    LOG(ERROR) << "firmware: could not find firmware for " << firmware;
-    for (const auto& message : attempted_paths_and_errors) {
-        LOG(ERROR) << message;
-    }
+    LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
 
     // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
     write(loading_fd, "-1", 2);
@@ -252,8 +115,7 @@
     }
     if (pid == 0) {
         Timer t;
-        auto firmware = GetFirmwarePath(uevent);
-        ProcessFirmwareEvent("/sys" + uevent.path, firmware);
+        ProcessFirmwareEvent(uevent);
         LOG(INFO) << "loading " << uevent.path << " took " << t;
         _exit(EXIT_SUCCESS);
     }
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index b4138f1..3996096 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -14,48 +14,32 @@
  * limitations under the License.
  */
 
-#pragma once
-
-#include <pwd.h>
+#ifndef _INIT_FIRMWARE_HANDLER_H
+#define _INIT_FIRMWARE_HANDLER_H
 
 #include <string>
 #include <vector>
 
-#include "result.h"
 #include "uevent.h"
 #include "uevent_handler.h"
 
 namespace android {
 namespace init {
 
-struct ExternalFirmwareHandler {
-    ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path)
-        : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {}
-    std::string devpath;
-    uid_t uid;
-    std::string handler_path;
-};
-
 class FirmwareHandler : public UeventHandler {
   public:
-    FirmwareHandler(std::vector<std::string> firmware_directories,
-                    std::vector<ExternalFirmwareHandler> external_firmware_handlers);
+    explicit FirmwareHandler(std::vector<std::string> firmware_directories);
     virtual ~FirmwareHandler() = default;
 
     void HandleUevent(const Uevent& uevent) override;
 
   private:
-    friend void FirmwareTestWithExternalHandler(const std::string& test_name,
-                                                bool expect_new_firmware);
-
-    Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid,
-                                           const Uevent& uevent) const;
-    std::string GetFirmwarePath(const Uevent& uevent) const;
-    void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
+    void ProcessFirmwareEvent(const Uevent& uevent);
 
     std::vector<std::string> firmware_directories_;
-    std::vector<ExternalFirmwareHandler> external_firmware_handlers_;
 };
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/firmware_handler_test.cpp b/init/firmware_handler_test.cpp
deleted file mode 100644
index 7bb603c..0000000
--- a/init/firmware_handler_test.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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 "firmware_handler.h"
-
-#include <stdlib.h>
-#include <iostream>
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-
-#include "uevent.h"
-
-using android::base::GetExecutablePath;
-using namespace std::literals;
-
-namespace android {
-namespace init {
-
-void FirmwareTestWithExternalHandler(const std::string& test_name, bool expect_new_firmware) {
-    auto test_path = GetExecutablePath() + " firmware " + test_name;
-    auto external_firmware_handler = ExternalFirmwareHandler(
-            "/devices/led/firmware/test_firmware001.bin", getuid(), test_path);
-
-    auto firmware_handler = FirmwareHandler({"/test"}, {external_firmware_handler});
-
-    auto uevent = Uevent{
-            .path = "/devices/led/firmware/test_firmware001.bin",
-            .firmware = "test_firmware001.bin",
-    };
-
-    if (expect_new_firmware) {
-        EXPECT_EQ("other_firmware001.bin", firmware_handler.GetFirmwarePath(uevent));
-    } else {
-        EXPECT_EQ("test_firmware001.bin", firmware_handler.GetFirmwarePath(uevent));
-    }
-
-    // Always test the base case that the handler isn't invoked if the devpath doesn't match.
-    auto uevent_different_path = Uevent{
-            .path = "/devices/led/not/mine",
-            .firmware = "test_firmware001.bin",
-    };
-    EXPECT_EQ("test_firmware001.bin", firmware_handler.GetFirmwarePath(uevent_different_path));
-}
-
-TEST(firmware_handler, HandleChange) {
-    FirmwareTestWithExternalHandler("HandleChange", true);
-}
-
-int HandleChange(int argc, char** argv) {
-    // Assert that the environment is set up correctly.
-    if (getenv("DEVPATH") != "/devices/led/firmware/test_firmware001.bin"s) {
-        std::cerr << "$DEVPATH not set correctly" << std::endl;
-        return EXIT_FAILURE;
-    }
-    if (getenv("FIRMWARE") != "test_firmware001.bin"s) {
-        std::cerr << "$FIRMWARE not set correctly" << std::endl;
-        return EXIT_FAILURE;
-    }
-    std::cout << "other_firmware001.bin" << std::endl;
-    return 0;
-}
-
-TEST(firmware_handler, HandleAbort) {
-    FirmwareTestWithExternalHandler("HandleAbort", false);
-}
-
-int HandleAbort(int argc, char** argv) {
-    abort();
-    return 0;
-}
-
-TEST(firmware_handler, HandleFailure) {
-    FirmwareTestWithExternalHandler("HandleFailure", false);
-}
-
-int HandleFailure(int argc, char** argv) {
-    std::cerr << "Failed" << std::endl;
-    return EXIT_FAILURE;
-}
-
-TEST(firmware_handler, HandleBadPath) {
-    FirmwareTestWithExternalHandler("HandleBadPath", false);
-}
-
-int HandleBadPath(int argc, char** argv) {
-    std::cout << "../firmware.bin";
-    return 0;
-}
-
-}  // namespace init
-}  // namespace android
-
-// init_test.cpp contains the main entry point for all init tests.
-int FirmwareTestChildMain(int argc, char** argv) {
-    if (argc < 3) {
-        return 1;
-    }
-
-#define RunTest(testname)                           \
-    if (argv[2] == std::string(#testname)) {        \
-        return android::init::testname(argc, argv); \
-    }
-
-    RunTest(HandleChange);
-    RunTest(HandleAbort);
-    RunTest(HandleFailure);
-    RunTest(HandleBadPath);
-
-#undef RunTest
-    return 1;
-}
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index ac44796..2b89940 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -24,18 +24,15 @@
 #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"
@@ -78,7 +75,7 @@
 
             if (S_ISDIR(info.st_mode)) {
                 is_dir = true;
-                auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+                auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
                 if (fd >= 0) {
                     auto subdir =
                             std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
@@ -94,50 +91,9 @@
     }
 }
 
-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, STDIN_FILENO);
-    dup2(fd, STDOUT_FILENO);
-    dup2(fd, STDERR_FILENO);
-    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) {
+bool ForceNormalBoot() {
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
     return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
@@ -152,7 +108,7 @@
 
     std::vector<std::pair<std::string, int>> errors;
 #define CHECKCALL(x) \
-    if ((x) != 0) errors.emplace_back(#x " failed", errno);
+    if (x != 0) errors.emplace_back(#x " failed", errno);
 
     // Clear the umask.
     umask(0);
@@ -170,8 +126,6 @@
 #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));
@@ -238,21 +192,7 @@
         old_root_dir.reset();
     }
 
-    Modprobe m({"/lib/modules"});
-    auto want_console = ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline);
-    if (!m.LoadListedModules(!want_console)) {
-        if (want_console) {
-            LOG(ERROR) << "Failed to load kernel modules, starting console";
-        } else {
-            LOG(FATAL) << "Failed to load kernel modules";
-        }
-    }
-
-    if (want_console) {
-        StartConsole();
-    }
-
-    if (ForceNormalBoot(cmdline)) {
+    if (ForceNormalBoot()) {
         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.
@@ -291,15 +231,12 @@
 
     SetInitAvbVersionInRecovery();
 
-    setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
-           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);
 
     const char* path = "/system/bin/init";
     const char* args[] = {path, "selinux_setup", nullptr};
-    auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
-    dup2(fd, STDOUT_FILENO);
-    dup2(fd, STDERR_FILENO);
-    close(fd);
     execv(path, const_cast<char**>(args));
 
     // execv() only returns if an error happened, in which case we
diff --git a/init/first_stage_init.h b/init/first_stage_init.h
index 7de816f..0476e44 100644
--- a/init/first_stage_init.h
+++ b/init/first_stage_init.h
@@ -21,7 +21,5 @@
 
 int FirstStageMain(int argc, char** argv);
 
-static constexpr char kEnvFirstStageStartedAt[] = "FIRST_STAGE_STARTED_AT";
-
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 9121bac..3e76556 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -36,7 +36,6 @@
 #include <fs_mgr_overlayfs.h>
 #include <libgsi/libgsi.h>
 #include <liblp/liblp.h>
-#include <libsnapshot/snapshot.h>
 
 #include "devices.h"
 #include "switch_root.h"
@@ -50,14 +49,12 @@
 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 android::fs_mgr::TransformFstabForDsu;
-using android::init::WriteFile;
-using android::snapshot::SnapshotManager;
 
 using namespace std::literals;
 
@@ -78,9 +75,8 @@
     bool InitDevices();
 
   protected:
-    ListenerAction HandleBlockDevice(const std::string& name, const Uevent&,
-                                     std::set<std::string>* required_devices);
-    bool InitRequiredDevices(std::set<std::string> devices);
+    ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
+    bool InitRequiredDevices();
     bool InitMappedDevice(const std::string& verity_device);
     bool InitDeviceMapper();
     bool CreateLogicalPartitions();
@@ -91,14 +87,14 @@
     bool TrySwitchSystemAsRoot();
     bool TrySkipMountingPartitions();
     bool IsDmLinearEnabled();
-    void GetDmLinearMetadataDevice(std::set<std::string>* devices);
+    bool GetDmLinearMetadataDevice();
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
     void UseGsiIfPresent();
 
-    ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);
+    ListenerAction UeventCallback(const Uevent& uevent);
 
     // Pure virtual functions.
-    virtual bool GetDmVerityDevices(std::set<std::string>* devices) = 0;
+    virtual bool GetDmVerityDevices() = 0;
     virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
 
     bool need_dm_verity_;
@@ -106,6 +102,7 @@
 
     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_;
@@ -117,7 +114,7 @@
     ~FirstStageMountVBootV1() override = default;
 
   protected:
-    bool GetDmVerityDevices(std::set<std::string>* devices) override;
+    bool GetDmVerityDevices() override;
     bool SetUpDmVerity(FstabEntry* fstab_entry) override;
 };
 
@@ -129,7 +126,7 @@
     ~FirstStageMountVBootV2() override = default;
 
   protected:
-    bool GetDmVerityDevices(std::set<std::string>* devices) override;
+    bool GetDmVerityDevices() override;
     bool SetUpDmVerity(FstabEntry* fstab_entry) override;
     bool InitAvbHandle();
 
@@ -247,19 +244,15 @@
 
     if (!InitDevices()) return false;
 
+    if (!CreateLogicalPartitions()) return false;
+
     if (!MountPartitions()) return false;
 
     return true;
 }
 
 bool FirstStageMount::InitDevices() {
-    std::set<std::string> devices;
-    GetDmLinearMetadataDevice(&devices);
-
-    if (!GetDmVerityDevices(&devices)) {
-        return false;
-    }
-    return InitRequiredDevices(std::move(devices));
+    return GetDmLinearMetadataDevice() && GetDmVerityDevices() && InitRequiredDevices();
 }
 
 bool FirstStageMount::IsDmLinearEnabled() {
@@ -269,46 +262,47 @@
     return false;
 }
 
-void FirstStageMount::GetDmLinearMetadataDevice(std::set<std::string>* devices) {
+bool FirstStageMount::GetDmLinearMetadataDevice() {
     // Add any additional devices required for dm-linear mappings.
     if (!IsDmLinearEnabled()) {
-        return;
-    }
-
-    devices->emplace(super_partition_name_);
-}
-
-// Creates devices with uevent->partition_name matching ones in the given set.
-// Found partitions will then be removed from it for the subsequent member
-// function to check which devices are NOT created.
-bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
-    if (!InitDeviceMapper()) {
-        return false;
-    }
-
-    if (devices.empty()) {
         return true;
     }
 
-    auto uevent_callback = [&, this](const Uevent& uevent) {
-        return UeventCallback(uevent, &devices);
-    };
+    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 (required_devices_partition_names_.empty()) {
+        return true;
+    }
+
+    if (IsDmLinearEnabled() || need_dm_verity_) {
+        if (!InitDeviceMapper()) {
+            return false;
+        }
+    }
+
+    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
     uevent_listener_.RegenerateUevents(uevent_callback);
 
-    // UeventCallback() will remove found partitions from |devices|. So if it
-    // isn't empty here, it means some partitions are not found.
-    if (!devices.empty()) {
+    // 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(devices, ", ");
+                  << android::base::Join(required_devices_partition_names_, ", ");
         Timer t;
         uevent_listener_.Poll(uevent_callback, 10s);
         LOG(INFO) << "Wait for partitions returned after " << t;
     }
 
-    if (!devices.empty()) {
+    if (!required_devices_partition_names_.empty()) {
         LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
-                   << android::base::Join(devices, ", ");
+                   << android::base::Join(required_devices_partition_names_, ", ");
         return false;
     }
 
@@ -341,20 +335,27 @@
 }
 
 bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
-    std::set<std::string> devices;
-
     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;
         }
-        devices.emplace(partition_name);
+        required_devices_partition_names_.emplace(partition_name);
     }
-    if (devices.empty()) {
+    if (required_devices_partition_names_.empty()) {
         return true;
     }
-    return InitRequiredDevices(std::move(devices));
+
+    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() {
@@ -367,21 +368,6 @@
         return false;
     }
 
-    if (SnapshotManager::IsSnapshotManagerNeeded()) {
-        auto sm = SnapshotManager::NewForFirstStageMount();
-        if (!sm) {
-            return false;
-        }
-        if (sm->NeedSnapshotsInFirstStageMount()) {
-            // When COW images are present for snapshots, they are stored on
-            // the data partition.
-            if (!InitRequiredDevices({"userdata"})) {
-                return false;
-            }
-            return sm->CreateLogicalAndSnapshotPartitions(lp_metadata_partition_);
-        }
-    }
-
     auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
     if (!metadata) {
         LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
@@ -393,21 +379,20 @@
     return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), lp_metadata_partition_);
 }
 
-ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent,
-                                                  std::set<std::string>* required_devices) {
+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->find(name);
-    if (iter != required_devices->end()) {
+    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->erase(iter);
+        required_devices_partition_names_.erase(iter);
         device_handler_->HandleUevent(uevent);
-        if (required_devices->empty()) {
+        if (required_devices_partition_names_.empty()) {
             return ListenerAction::kStop;
         } else {
             return ListenerAction::kContinue;
@@ -416,19 +401,18 @@
     return ListenerAction::kContinue;
 }
 
-ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent,
-                                               std::set<std::string>* required_devices) {
+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, required_devices);
+        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, required_devices);
+            return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent);
         }
     }
     // Not found a partition or find an unneeded partition, continue to find others.
@@ -511,7 +495,14 @@
 // 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() {
-    UseGsiIfPresent();
+    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";
@@ -534,17 +525,6 @@
 }
 
 bool FirstStageMount::MountPartitions() {
-    // Mount /metadata before creating logical partitions, since we need to
-    // know whether a snapshot merge is in progress.
-    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
-        return entry.mount_point == "/metadata";
-    });
-    if (metadata_partition != fstab_.end()) {
-        MountPartition(metadata_partition, true /* erase_same_mounts */);
-    }
-
-    if (!CreateLogicalPartitions()) return false;
-
     if (!TrySwitchSystemAsRoot()) return false;
 
     if (!SkipMountingPartitions(&fstab_)) return false;
@@ -585,7 +565,17 @@
     const auto devices = fs_mgr_overlayfs_required_devices(&fstab_);
     for (auto const& device : devices) {
         if (android::base::StartsWith(device, "/dev/block/by-name/")) {
-            InitRequiredDevices({basename(device.c_str())});
+            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);
         }
@@ -597,14 +587,14 @@
 }
 
 void FirstStageMount::UseGsiIfPresent() {
-    std::string error;
+    std::string metadata_file, error;
 
-    if (!android::gsi::CanBootIntoGsi(&error)) {
+    if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) {
         LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
         return;
     }
 
-    auto metadata = android::fs_mgr::ReadFromImageFile(gsi::kDsuLpMetadataFile);
+    auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str());
     if (!metadata) {
         LOG(ERROR) << "GSI partition layout could not be read";
         return;
@@ -614,11 +604,25 @@
         return;
     }
 
-    // Find the super name. PartitionOpener will ensure this translates to the
-    // correct block device path.
+    // Device-mapper might not be ready if the device doesn't use DAP or verity
+    // (for example, hikey).
+    if (access("/dev/device-mapper", F_OK) && !InitDeviceMapper()) {
+        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());
-    auto super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
-    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_name)) {
+    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;
     }
@@ -628,20 +632,19 @@
         return;
     }
 
-    std::string lp_names = "";
-    std::vector<std::string> dsu_partitions;
-    for (auto&& partition : metadata->partitions) {
-        auto name = fs_mgr::GetPartitionName(partition);
-        dsu_partitions.push_back(name);
-        lp_names += name + ",";
+    // 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);
     }
-    // Publish the logical partition names for TransformFstabForDsu
-    WriteFile(gsi::kGsiLpNamesFile, lp_names);
-    TransformFstabForDsu(&fstab_, dsu_partitions);
+    fstab_.emplace_back(BuildGsiSystemFstabEntry());
     gsi_not_on_userdata_ = (super_name != "userdata");
 }
 
-bool FirstStageMountVBootV1::GetDmVerityDevices(std::set<std::string>* devices) {
+bool FirstStageMountVBootV1::GetDmVerityDevices() {
+    std::string verity_loc_device;
     need_dm_verity_ = false;
 
     for (const auto& fstab_entry : fstab_) {
@@ -654,16 +657,32 @@
         if (fstab_entry.fs_mgr_flags.verify) {
             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_entry.verity_loc.empty()) {
+            if (verity_loc_device.empty()) {
+                verity_loc_device = fstab_entry.verity_loc;
+            } else if (verity_loc_device != fstab_entry.verity_loc) {
+                LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
+                           << fstab_entry.verity_loc;
+                return false;
+            }
+        }
     }
 
-    // Includes the partition names of fstab records.
+    // 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 (const auto& fstab_entry : fstab_) {
         if (!fstab_entry.fs_mgr_flags.logical) {
-            devices->emplace(basename(fstab_entry.blk_device.c_str()));
+            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
         }
     }
 
+    if (!verity_loc_device.empty()) {
+        required_devices_partition_names_.emplace(basename(verity_loc_device.c_str()));
+    }
+
     return true;
 }
 
@@ -711,7 +730,7 @@
     }
 }
 
-bool FirstStageMountVBootV2::GetDmVerityDevices(std::set<std::string>* devices) {
+bool FirstStageMountVBootV2::GetDmVerityDevices() {
     need_dm_verity_ = false;
 
     std::set<std::string> logical_partitions;
@@ -725,7 +744,7 @@
             // Don't try to find logical partitions via uevent regeneration.
             logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));
         } else {
-            devices->emplace(basename(fstab_entry.blk_device.c_str()));
+            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
         }
     }
 
@@ -742,11 +761,11 @@
             if (logical_partitions.count(partition_name)) {
                 continue;
             }
-            // devices is of type std::set so it's not an issue to emplace a
-            // partition twice. e.g., /vendor might be in both places:
+            // 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
-            devices->emplace(partition_name);
+            required_devices_partition_names_.emplace(partition_name);
         }
     }
     return true;
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
deleted file mode 100644
index 7820f3d..0000000
--- a/init/fscrypt_init_extensions.cpp
+++ /dev/null
@@ -1,149 +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 "fscrypt_init_extensions.h"
-
-#include <dirent.h>
-#include <errno.h>
-#include <fts.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#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 <cutils/properties.h>
-#include <cutils/sockets.h>
-#include <fscrypt/fscrypt.h>
-#include <keyutils.h>
-#include <logwrap/logwrap.h>
-
-#define TAG "fscrypt"
-
-using namespace android::fscrypt;
-
-bool FscryptInstallKeyring() {
-    key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, KEY_SPEC_SESSION_KEYRING);
-
-    if (device_keyring == -1) {
-        PLOG(ERROR) << "Failed to create keyring";
-        return false;
-    }
-    LOG(INFO) << "Keyring created with id " << device_keyring << " in process " << getpid();
-    return true;
-}
-
-// TODO(b/139378601): use a single central implementation of this.
-static void delete_dir_contents(const std::string& dir) {
-    char* const paths[2] = {const_cast<char*>(dir.c_str()), nullptr};
-    FTS* fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
-    FTSENT* cur;
-    while ((cur = fts_read(fts)) != nullptr) {
-        if (cur->fts_info == FTS_ERR) {
-            PLOG(ERROR) << "fts_read";
-            break;
-        }
-        if (dir == cur->fts_path) {
-            continue;
-        }
-        switch (cur->fts_info) {
-            case FTS_D:
-                break;  // Ignore these
-            case FTS_DP:
-                if (rmdir(cur->fts_path) == -1) {
-                    PLOG(ERROR) << "rmdir " << cur->fts_path;
-                }
-                break;
-            default:
-                PLOG(ERROR) << "FTS unexpected type " << cur->fts_info << " at " << cur->fts_path;
-                if (rmdir(cur->fts_path) != -1) break;
-                // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
-                FALLTHROUGH_INTENDED;
-            case FTS_F:
-            case FTS_SL:
-            case FTS_SLNONE:
-                if (unlink(cur->fts_path) == -1) {
-                    PLOG(ERROR) << "unlink " << cur->fts_path;
-                }
-                break;
-        }
-    }
-
-    if (fts_close(fts) != 0) {
-        PLOG(ERROR) << "fts_close";
-    }
-}
-
-// Look up an encryption policy  The policy (key reference
-// and encryption options) to use is read from files that were written by vold.
-static bool LookupPolicy(const std::string& ref_basename, EncryptionPolicy* policy) {
-    std::string ref_filename = std::string("/data") + ref_basename;
-    if (!android::base::ReadFileToString(ref_filename, &policy->key_raw_ref)) {
-        LOG(ERROR) << "Unable to read system policy with name " << ref_filename;
-        return false;
-    }
-
-    auto options_filename = std::string("/data") + fscrypt_key_mode;
-    std::string options_string;
-    if (!android::base::ReadFileToString(options_filename, &options_string)) {
-        LOG(ERROR) << "Cannot read encryption options string";
-        return false;
-    }
-    if (!ParseOptions(options_string, &policy->options)) {
-        LOG(ERROR) << "Invalid encryption options string: " << options_string;
-        return false;
-    }
-    return true;
-}
-
-static bool EnsurePolicyOrLog(const EncryptionPolicy& policy, const std::string& dir) {
-    if (!EnsurePolicy(policy, dir)) {
-        std::string ref_hex;
-        BytesToHex(policy.key_raw_ref, &ref_hex);
-        LOG(ERROR) << "Setting " << ref_hex << " policy on " << dir << " failed!";
-        return false;
-    }
-    return true;
-}
-
-static bool SetPolicyOn(const std::string& ref_basename, const std::string& dir) {
-    EncryptionPolicy policy;
-    if (!LookupPolicy(ref_basename, &policy)) return false;
-    if (!EnsurePolicyOrLog(policy, dir)) return false;
-    return true;
-}
-
-bool FscryptSetDirectoryPolicy(const std::string& ref_basename, FscryptAction action,
-                               const std::string& dir) {
-    if (action == FscryptAction::kNone) {
-        return true;
-    }
-    if (SetPolicyOn(ref_basename, dir) || action == FscryptAction::kAttempt) {
-        return true;
-    }
-    if (action == FscryptAction::kDeleteIfNecessary) {
-        LOG(ERROR) << "Setting policy failed, deleting: " << dir;
-        delete_dir_contents(dir);
-        return SetPolicyOn(ref_basename, dir);
-    }
-    return false;
-}
diff --git a/init/host_builtin_map.py b/init/host_builtin_map.py
deleted file mode 100755
index 6afcb17..0000000
--- a/init/host_builtin_map.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env python
-"""Generates the builtins map to be used by host_init_verifier.
-
-It copies the builtin function map from builtins.cpp, then replaces do_xxx() functions with the
-equivalent check_xxx() if found in check_builtins.cpp.
-
-"""
-
-import re
-import argparse
-
-parser = argparse.ArgumentParser('host_builtin_map.py')
-parser.add_argument('--builtins', required=True, help='Path to builtins.cpp')
-parser.add_argument('--check_builtins', required=True, help='Path to check_builtins.cpp')
-args = parser.parse_args()
-
-CHECK_REGEX = re.compile(r'.+check_(\S+)\(.+')
-check_functions = []
-with open(args.check_builtins) as check_file:
-  for line in check_file:
-    match = CHECK_REGEX.match(line)
-    if match:
-      check_functions.append(match.group(1))
-
-function_map = []
-with open(args.builtins) as builtins_file:
-  in_function_map = False
-  for line in builtins_file:
-    if '// Builtin-function-map start' in line:
-      in_function_map = True
-    elif '// Builtin-function-map end' in line:
-      in_function_map = False
-    elif in_function_map:
-      function_map.append(line)
-
-DO_REGEX = re.compile(r'.+do_([^\}]+).+')
-FUNCTION_REGEX = re.compile(r'(do_[^\}]+)')
-for line in function_map:
-  match = DO_REGEX.match(line)
-  if match:
-    if match.group(1) in check_functions:
-      print line.replace('do_', 'check_'),
-    else:
-      print FUNCTION_REGEX.sub('check_stub', line),
-  else:
-    print line,
diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp
index aa80199..93e363f 100644
--- a/init/host_import_parser.cpp
+++ b/init/host_import_parser.cpp
@@ -23,16 +23,16 @@
 namespace android {
 namespace init {
 
-Result<void> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
-                                            int) {
+Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,
+                                               int) {
     if (args.size() != 2) {
         return Error() << "single argument needed for import\n";
     }
 
-    return {};
+    return Success();
 }
 
-Result<void> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+Result<Success> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {
     return Error() << "Unexpected line found after import statement";
 }
 
diff --git a/init/host_import_parser.h b/init/host_import_parser.h
index d6f7286..52b8891 100644
--- a/init/host_import_parser.h
+++ b/init/host_import_parser.h
@@ -27,8 +27,8 @@
 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;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
 };
 
 }  // namespace init
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
new file mode 100644
index 0000000..b85e54a
--- /dev/null
+++ b/init/host_init_stubs.cpp
@@ -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.
+ */
+
+#include "host_init_stubs.h"
+
+#include <android-base/properties.h>
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list) {
+    return 0;
+}
+
+namespace android {
+namespace init {
+
+// init.h
+std::string default_console = "/dev/console";
+
+// property_service.h
+bool CanReadProperty(const std::string& source_context, const std::string& name) {
+    return true;
+}
+uint32_t SetProperty(const std::string& key, const std::string& value) {
+    android::base::SetProperty(key, value);
+    return 0;
+}
+uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
+uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
+                           std::string*) {
+    return 0;
+}
+
+// selinux.h
+int SelinuxGetVendorAndroidVersion() {
+    return 10000;
+}
+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 9b33a1c..f6e9676 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_HOST_INIT_STUBS_H
+#define _INIT_HOST_INIT_STUBS_H
 
 #include <stddef.h>
 #include <sys/socket.h>
@@ -22,54 +23,39 @@
 
 #include <string>
 
-#include <android-base/properties.h>
-
 // android/api-level.h
 #define __ANDROID_API_P__ 28
-#define __ANDROID_API_Q__ 29
-#define __ANDROID_API_R__ 30
 
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
 
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list);
+
 namespace android {
 namespace init {
 
 // init.h
-inline void TriggerShutdown(const std::string&) {
-    abort();
-}
+extern std::string default_console;
 
 // property_service.h
-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;
-}
+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);
 
 // reboot_utils.h
 inline void SetFatalRebootTarget() {}
-inline void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
+inline void __attribute__((noreturn)) InitFatalReboot() {
     abort();
 }
 
-// selabel.h
-inline void SelabelInitialize() {}
-inline bool SelabelLookupFileContext(const std::string&, int, std::string*) {
-    return false;
-}
-
 // selinux.h
-inline int SelinuxGetVendorAndroidVersion() {
-    return 10000;
-}
+int SelinuxGetVendorAndroidVersion();
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 522709e..8407729 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -15,14 +15,11 @@
 //
 
 #include <errno.h>
-#include <getopt.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <fstream>
 #include <iostream>
-#include <iterator>
 #include <string>
 #include <vector>
 
@@ -30,20 +27,15 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
-#include <hidl/metadata.h>
 
 #include "action.h"
 #include "action_manager.h"
 #include "action_parser.h"
-#include "check_builtins.h"
 #include "host_import_parser.h"
 #include "host_init_stubs.h"
-#include "interface_utils.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"
@@ -54,9 +46,9 @@
 using android::base::ReadFileToString;
 using android::base::Split;
 
-static std::vector<std::string> passwd_files;
+static std::string passwd_file;
 
-static std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {
+static std::vector<std::pair<std::string, int>> GetVendorPasswd() {
     std::string passwd;
     if (!ReadFileToString(passwd_file, &passwd)) {
         return {};
@@ -78,16 +70,6 @@
     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] = "";
@@ -136,104 +118,41 @@
 namespace android {
 namespace init {
 
-static Result<void> check_stub(const BuiltinArguments& args) {
-    return {};
+static Result<Success> do_stub(const BuiltinArguments& args) {
+    return Success();
 }
 
 #include "generated_stub_builtin_function_map.h"
 
-void PrintUsage() {
-    std::cout << "usage: host_init_verifier [-p 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"
-              << std::endl;
-}
-
-Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy() {
-    InterfaceInheritanceHierarchyMap result;
-    for (const HidlInterfaceMetadata& iface : HidlInterfaceMetadata::all()) {
-        std::set<FQName> inherited_interfaces;
-        for (const std::string& intf : iface.inherited) {
-            FQName fqname;
-            if (!fqname.setTo(intf)) {
-                return Error() << "Unable to parse interface '" << intf << "'";
-            }
-            inherited_interfaces.insert(fqname);
-        }
-        FQName fqname;
-        if (!fqname.setTo(iface.name)) {
-            return Error() << "Unable to parse interface '" << iface.name << "'";
-        }
-        result[fqname] = inherited_interfaces;
-    }
-
-    return result;
-}
-
 int main(int argc, char** argv) {
     android::base::InitLogging(argv, &android::base::StdioLogger);
     android::base::SetMinimumLogSeverity(android::base::ERROR);
 
-    while (true) {
-        static const struct option long_options[] = {
-                {"help", no_argument, nullptr, 'h'},
-                {nullptr, 0, nullptr, 0},
-        };
-
-        int arg = getopt_long(argc, argv, "p:", long_options, nullptr);
-
-        if (arg == -1) {
-            break;
-        }
-
-        switch (arg) {
-            case 'h':
-                PrintUsage();
-                return EXIT_FAILURE;
-            case 'p':
-                passwd_files.emplace_back(optarg);
-                break;
-            default:
-                std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
-                return EXIT_FAILURE;
-        }
-    }
-
-    argc -= optind;
-    argv += optind;
-
-    if (argc != 1) {
-        PrintUsage();
+    if (argc != 2 && argc != 3) {
+        LOG(ERROR) << "Usage: " << argv[0] << " <init rc file> [passwd file]";
         return EXIT_FAILURE;
     }
 
-    auto interface_inheritance_hierarchy_map = ReadInterfaceInheritanceHierarchy();
-    if (!interface_inheritance_hierarchy_map) {
-        LOG(ERROR) << interface_inheritance_hierarchy_map.error();
-        return EXIT_FAILURE;
+    if (argc == 3) {
+        passwd_file = argv[2];
     }
-    SetKnownInterfaces(*interface_inheritance_hierarchy_map);
 
-    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
+    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, *interface_inheritance_hierarchy_map));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<HostImportParser>());
 
-    if (!parser.ParseConfigFileInsecure(*argv)) {
-        LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
+    if (!parser.ParseConfigFileInsecure(argv[1])) {
+        LOG(ERROR) << "Failed to open init rc script '" << argv[1] << "'";
         return EXIT_FAILURE;
     }
-    size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
-    if (failures > 0) {
-        LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures
-                   << " errors";
+    if (parser.parse_error_count() > 0) {
+        LOG(ERROR) << "Failed to parse init script '" << argv[1] << "' with "
+                   << parser.parse_error_count() << " errors";
         return EXIT_FAILURE;
     }
     return EXIT_SUCCESS;
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index 1a43508..fb3185e 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -23,24 +23,25 @@
 namespace android {
 namespace init {
 
-Result<void> ImportParser::ParseSection(std::vector<std::string>&& args,
-                                        const std::string& filename, int line) {
+Result<Success> 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";
     }
 
-    auto conf_file = ExpandProps(args[1]);
-    if (!conf_file) {
-        return Error() << "Could not expand import: " << conf_file.error();
+    std::string conf_file;
+    bool ret = expand_props(args[1], &conf_file);
+    if (!ret) {
+        return Error() << "error while expanding import";
     }
 
-    LOG(INFO) << "Added '" << *conf_file << "' to import list";
+    LOG(INFO) << "Added '" << conf_file << "' to import list";
     if (filename_.empty()) filename_ = filename;
-    imports_.emplace_back(std::move(*conf_file), line);
-    return {};
+    imports_.emplace_back(std::move(conf_file), line);
+    return Success();
 }
 
-Result<void> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
+Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
     return Error() << "Unexpected line found after import statement";
 }
 
diff --git a/init/import_parser.h b/init/import_parser.h
index 5bf9c6c..7bc72e6 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -28,9 +28,9 @@
 class ImportParser : public SectionParser {
   public:
     ImportParser(Parser* parser) : parser_(parser) {}
-    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
-                              int line) override;
-    Result<void> ParseLineSection(std::vector<std::string>&&, int) override;
+    Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
+                                 int line) override;
+    Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
     void EndFile() override;
 
   private:
diff --git a/init/init.cpp b/init/init.cpp
index 6ea2d00..6b03bc9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <seccomp_policy.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
@@ -27,19 +28,13 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.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>
@@ -52,27 +47,24 @@
 #include <processgroup/setup.h>
 #include <selinux/android.h>
 
+#ifndef RECOVERY
+#include <binder/ProcessState.h>
+#endif
+
 #include "action_parser.h"
-#include "builtins.h"
+#include "boringssl_self_test.h"
 #include "epoll.h"
-#include "first_stage_init.h"
 #include "first_stage_mount.h"
 #include "import_parser.h"
 #include "keychords.h"
-#include "lmkd_service.h"
 #include "mount_handler.h"
 #include "mount_namespace.h"
 #include "property_service.h"
-#include "proto_utils.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 "system/core/init/property_service.pb.h"
 #include "util.h"
 
 using namespace std::chrono_literals;
@@ -93,17 +85,21 @@
 
 static char qemu[32];
 
+std::string default_console = "/dev/console";
+
 static int signal_fd = -1;
-static int property_fd = -1;
 
 static std::unique_ptr<Timer> waiting_for_prop(nullptr);
 static std::string wait_prop_name;
 static std::string wait_prop_value;
+static bool shutting_down;
 static std::string shutdown_command;
 static bool do_shutdown = false;
 static bool load_debug_prop = false;
 
-static std::unique_ptr<Subcontext> subcontext;
+std::vector<std::string> late_import_paths;
+
+static std::vector<Subcontext>* subcontexts;
 
 void DumpState() {
     ServiceList::GetInstance().DumpState();
@@ -113,10 +109,8 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
-                                               &service_list, subcontext.get(), std::nullopt));
-    parser.AddSectionParser("on",
-                            std::make_unique<ActionParser>(&action_manager, subcontext.get()));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     return parser;
@@ -126,8 +120,7 @@
 Parser CreateServiceOnlyParser(ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
-                                               &service_list, subcontext.get(), std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
     return parser;
 }
 
@@ -140,12 +133,12 @@
         if (!parser.ParseConfig("/system/etc/init")) {
             late_import_paths.emplace_back("/system/etc/init");
         }
-        // late_import is available only in Q and earlier release. As we don't
-        // have system_ext in those versions, skip late_import for system_ext.
-        parser.ParseConfig("/system_ext/etc/init");
         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");
         }
@@ -180,16 +173,6 @@
     waiting_for_prop.reset();
 }
 
-void TriggerShutdown(const std::string& command) {
-    // We can't call HandlePowerctlMessage() directly in this function,
-    // because it modifies the contents of the action queue, which can cause the action queue
-    // to get into a bad state if this function is called from a command being executed by the
-    // action queue.  Instead we set this flag and ensure that shutdown happens before the next
-    // command is run in the main init loop.
-    shutdown_command = command;
-    do_shutdown = true;
-}
-
 void property_changed(const std::string& name, const std::string& value) {
     // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
     // This is to ensure that init will always and immediately shutdown/reboot, regardless of
@@ -198,19 +181,20 @@
     // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
     // commands to be executed.
     if (name == "sys.powerctl") {
-        TriggerShutdown(value);
+        // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
+        // because it modifies the contents of the action queue, which can cause the action queue
+        // to get into a bad state if this function is called from a command being executed by the
+        // action queue.  Instead we set this flag and ensure that shutdown happens before the next
+        // command is run in the main init loop.
+        // TODO: once property service is removed from init, this will never happen from a builtin,
+        // but rather from a callback from the property service socket, in which case this hack can
+        // go away.
+        shutdown_command = value;
+        do_shutdown = true;
     }
 
     if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
 
-    // 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 '" << wait_prop_name << "=" << wait_prop_value
@@ -250,18 +234,18 @@
     return next_process_action_time;
 }
 
-static Result<void> DoControlStart(Service* service) {
+static Result<Success> DoControlStart(Service* service) {
     return service->Start();
 }
 
-static Result<void> DoControlStop(Service* service) {
+static Result<Success> DoControlStop(Service* service) {
     service->Stop();
-    return {};
+    return Success();
 }
 
-static Result<void> DoControlRestart(Service* service) {
+static Result<Success> DoControlRestart(Service* service) {
     service->Restart();
-    return {};
+    return Success();
 }
 
 enum class ControlTarget {
@@ -271,16 +255,16 @@
 
 struct ControlMessageFunction {
     ControlTarget target;
-    std::function<Result<void>(Service*)> action;
+    std::function<Result<Success>(Service*)> action;
 };
 
 static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
     // clang-format off
     static const std::map<std::string, ControlMessageFunction> control_message_functions = {
         {"sigstop_on",        {ControlTarget::SERVICE,
-                               [](auto* service) { service->set_sigstop(true); return Result<void>{}; }}},
+                               [](auto* service) { service->set_sigstop(true); return Success(); }}},
         {"sigstop_off",       {ControlTarget::SERVICE,
-                               [](auto* service) { service->set_sigstop(false); return Result<void>{}; }}},
+                               [](auto* service) { service->set_sigstop(false); return Success(); }}},
         {"start",             {ControlTarget::SERVICE,   DoControlStart}},
         {"stop",              {ControlTarget::SERVICE,   DoControlStop}},
         {"restart",           {ControlTarget::SERVICE,   DoControlRestart}},
@@ -293,13 +277,13 @@
     return control_message_functions;
 }
 
-bool HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
+void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
     const auto& map = get_control_message_map();
     const auto it = map.find(msg);
 
     if (it == map.end()) {
         LOG(ERROR) << "Unknown control msg '" << msg << "'";
-        return false;
+        return;
     }
 
     std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
@@ -311,6 +295,9 @@
         process_cmdline = "unknown process";
     }
 
+    LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
+              << " (" << process_cmdline << ")";
+
     const ControlMessageFunction& function = it->second;
 
     Service* svc = nullptr;
@@ -323,37 +310,51 @@
             svc = ServiceList::GetInstance().FindInterface(name);
             break;
         default:
-            LOG(ERROR) << "Invalid function target from static map key ctl." << msg << ": "
+            LOG(ERROR) << "Invalid function target from static map key '" << msg << "': "
                        << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
-            return false;
+            return;
     }
 
     if (svc == nullptr) {
-        LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg
-                   << " from pid: " << pid << " (" << process_cmdline << ")";
-        return false;
+        LOG(ERROR) << "Could not find '" << name << "' for ctl." << msg;
+        return;
     }
 
     if (auto result = function.action(svc); !result) {
-        LOG(ERROR) << "Control message: Could not ctl." << msg << " for '" << name
-                   << "' from pid: " << pid << " (" << process_cmdline << "): " << result.error();
-        return false;
+        LOG(ERROR) << "Could not ctl." << msg << " for '" << name << "': " << result.error();
     }
-
-    LOG(INFO) << "Control message: Processed ctl." << msg << " for '" << name
-              << "' from pid: " << pid << " (" << process_cmdline << ")";
-    return true;
 }
 
-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 << "'";
+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;
     }
 
-    return {};
+    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
+    return Success();
 }
 
-static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
+static Result<Success> console_init_action(const BuiltinArguments& args) {
+    std::string console = GetProperty("ro.boot.console", "");
+    if (!console.empty()) {
+        default_console = "/dev/" + console;
+    }
+    return Success();
+}
+
+static Result<Success> 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);
@@ -361,7 +362,7 @@
         return ErrnoError() << "Failed to setup cgroups";
     }
 
-    return {};
+    return Success();
 }
 
 static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
@@ -445,16 +446,34 @@
     if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static Result<void> property_enable_triggers_action(const BuiltinArguments& args) {
+static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
     /* Enable property triggers. */
     property_triggers_enabled = 1;
-    return {};
+    return Success();
 }
 
-static Result<void> queue_property_triggers_action(const BuiltinArguments& args) {
+static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
     ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
     ActionManager::GetInstance().QueueAllPropertyActions();
-    return {};
+    return Success();
+}
+
+static Result<Success> 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 Success();
 }
 
 // Set the UDC controller for the ConfigFS USB Gadgets.
@@ -581,121 +600,38 @@
     }
 }
 
+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));
-}
-
-void SendLoadPersistentPropertiesMessage() {
-    auto init_message = InitMessage{};
-    init_message.set_load_persistent_properties(true);
-    if (auto result = SendMessage(property_fd, init_message); !result) {
-        LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
-    }
-}
-
-void SendStopSendingMessagesMessage() {
-    auto init_message = InitMessage{};
-    init_message.set_stop_sending_messages(true);
-    if (auto result = SendMessage(property_fd, init_message); !result) {
-        LOG(ERROR) << "Failed to send 'stop sending messages' message: " << result.error();
-    }
-}
-
-void SendStartSendingMessagesMessage() {
-    auto init_message = InitMessage{};
-    init_message.set_start_sending_messages(true);
-    if (auto result = SendMessage(property_fd, init_message); !result) {
-        LOG(ERROR) << "Failed to send 'start sending messages' message: " << result.error();
-    }
-}
-
-static void HandlePropertyFd() {
-    auto message = ReadMessage(property_fd);
-    if (!message) {
-        LOG(ERROR) << "Could not read message from property service: " << message.error();
-        return;
-    }
-
-    auto property_message = PropertyMessage{};
-    if (!property_message.ParseFromString(*message)) {
-        LOG(ERROR) << "Could not parse message from property service";
-        return;
-    }
-
-    switch (property_message.msg_case()) {
-        case PropertyMessage::kControlMessage: {
-            auto& control_message = property_message.control_message();
-            bool success = HandleControlMessage(control_message.msg(), control_message.name(),
-                                                control_message.pid());
-
-            uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
-            if (control_message.has_fd()) {
-                int fd = control_message.fd();
-                TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
-                close(fd);
-            }
-            break;
-        }
-        case PropertyMessage::kChangedMessage: {
-            auto& changed_message = property_message.changed_message();
-            property_changed(changed_message.name(), changed_message.value());
-            break;
-        }
-        default:
-            LOG(ERROR) << "Unknown message type from property service: "
-                       << property_message.msg_case();
-    }
-}
-
 int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
     }
 
-    boot_clock::time_point start_time = boot_clock::now();
-
     SetStdioToDevNull(argv);
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
-    // Will handle EPIPE at the time of write by checking the errno
-    signal(SIGPIPE, SIG_IGN);
-
     // Set init and its forked children's oom_adj.
-    if (auto result =
-                WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
-        !result) {
-        LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
-                   << " to /proc/1/oom_score_adj: " << result.error();
+    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.
@@ -715,8 +651,9 @@
     // used by init as well as the current required properties.
     export_kernel_boot_props();
 
-    // Make the time that init stages started available for bootstat to log.
-    RecordStageBoottimes(start_time);
+    // 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"));
 
     // Set libavb version for Framework-only OTA match in Treble build.
     const char* avb_version = getenv("INIT_AVB_VERSION");
@@ -729,6 +666,8 @@
     }
 
     // Clean up our environment.
+    unsetenv("INIT_STARTED_AT");
+    unsetenv("INIT_SELINUX_TOOK");
     unsetenv("INIT_AVB_VERSION");
     unsetenv("INIT_FORCE_DEBUGGABLE");
 
@@ -748,23 +687,18 @@
     UmountDebugRamdisk();
     fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
-
-    StartPropertyService(&property_fd);
-    if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result) {
-        LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
-    }
-
+    StartPropertyService(&epoll);
     MountHandler mount_handler(&epoll);
     set_usb_controller();
 
-    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
+    const BuiltinFunctionMap function_map;
     Action::set_function_map(&function_map);
 
     if (!SetupMountNamespaces()) {
         PLOG(FATAL) << "SetupMountNamespaces failed";
     }
 
-    subcontext = InitializeSubcontext();
+    subcontexts = InitializeSubcontexts();
 
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sm = ServiceList::GetInstance();
@@ -784,7 +718,6 @@
 
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
 
-    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
@@ -792,24 +725,32 @@
     // ... so that we can start queuing up actions that require stuff from /dev.
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
     am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
+    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     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");
+        [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
+            for (const auto& svc : ServiceList::GetInstance()) {
+                keychords.Register(svc->keycodes());
+            }
+            keychords.Start(&epoll, HandleKeychord);
+            return Success();
+        },
+        "KeychordInit");
+    am.QueueBuiltinAction(console_init_action, "console_init");
 
     // Trigger all the boot actions to get us started.
     am.QueueEventTrigger("init");
 
+    // Starting the BoringSSL self test, for NIAP certification compliance.
+    am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
+
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
     am.QueueBuiltinAction(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") {
@@ -825,16 +766,18 @@
         // By default, sleep until something happens.
         auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
 
-        if (do_shutdown && !IsShuttingDown()) {
+        if (do_shutdown && !shutting_down) {
             do_shutdown = false;
-            HandlePowerctlMessage(shutdown_command);
+            if (HandlePowerctlMessage(shutdown_command)) {
+                shutting_down = true;
+            }
         }
 
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
         }
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
-            if (!IsShuttingDown()) {
+            if (!shutting_down) {
                 auto next_process_action_time = HandleProcessActions();
 
                 // If there's a process that needs restarting, wake up in time for that.
@@ -849,17 +792,8 @@
             if (am.HasMoreCommands()) epoll_timeout = 0ms;
         }
 
-        auto pending_functions = epoll.Wait(epoll_timeout);
-        if (!pending_functions) {
-            LOG(ERROR) << pending_functions.error();
-        } else if (!pending_functions->empty()) {
-            // We always reap children before responding to the other pending functions. This is to
-            // prevent a race where other daemons see that a service has exited and ask init to
-            // start it again via ctl.start before init has reaped it.
-            ReapAnyOutstandingChildren();
-            for (const auto& function : *pending_functions) {
-                (*function)();
-            }
+        if (auto result = epoll.Wait(epoll_timeout); !result) {
+            LOG(ERROR) << result.error();
         }
     }
 
diff --git a/init/init.h b/init/init.h
index d884a94..a76da20 100644
--- a/init/init.h
+++ b/init/init.h
@@ -14,24 +14,35 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_INIT_H
+#define _INIT_INIT_H
 
 #include <sys/types.h>
 
+#include <functional>
 #include <string>
+#include <vector>
 
 #include "action.h"
 #include "action_manager.h"
 #include "parser.h"
-#include "service_list.h"
+#include "service.h"
 
 namespace android {
 namespace init {
 
+// Note: These globals are *only* valid in init, so they should not be used in ueventd
+// or any files that may be included in ueventd, 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 TriggerShutdown(const std::string& command);
+void HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
+
+void property_changed(const std::string& name, const std::string& value);
 
 bool start_waiting_for_property(const char *name, const char *value);
 
@@ -39,11 +50,9 @@
 
 void ResetWaitForProp();
 
-void SendLoadPersistentPropertiesMessage();
-void SendStopSendingMessagesMessage();
-void SendStartSendingMessagesMessage();
-
 int SecondStageMain(int argc, char** argv);
 
 }  // namespace init
 }  // namespace android
+
+#endif  /* _INIT_INIT_H */
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 9f63e4f..c2f0c41 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -22,14 +22,12 @@
 #include "action.h"
 #include "action_manager.h"
 #include "action_parser.h"
-#include "builtin_arguments.h"
 #include "builtins.h"
 #include "import_parser.h"
 #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"
 
 namespace android {
@@ -37,15 +35,14 @@
 
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
-void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,
+void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
               const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     ActionManager am;
 
     Action::set_function_map(&test_function_map);
 
     Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -60,7 +57,7 @@
     }
 }
 
-void TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map,
+void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
                   const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
@@ -76,13 +73,8 @@
 pass_test
 )init";
 
-    auto do_pass_test = [&expect_true](const BuiltinArguments&) {
-        expect_true = true;
-        return Result<void>{};
-    };
-    BuiltinFunctionMap test_function_map = {
-            {"pass_test", {0, 0, {false, do_pass_test}}},
-    };
+    TestFunctionMap test_function_map;
+    test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
@@ -93,26 +85,6 @@
     EXPECT_TRUE(expect_true);
 }
 
-TEST(init, WrongEventTrigger) {
-    std::string init_script =
-            R"init(
-on boot:
-pass_test
-)init";
-
-    TemporaryFile tf;
-    ASSERT_TRUE(tf.fd != -1);
-    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
-
-    ActionManager am;
-
-    Parser parser;
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
-
-    ASSERT_TRUE(parser.ParseConfig(tf.path));
-    ASSERT_EQ(1u, parser.parse_error_count());
-}
-
 TEST(init, EventTriggerOrder) {
     std::string init_script =
         R"init(
@@ -128,24 +100,10 @@
 )init";
 
     int num_executed = 0;
-    auto do_execute_first = [&num_executed](const BuiltinArguments&) {
-        EXPECT_EQ(0, num_executed++);
-        return Result<void>{};
-    };
-    auto do_execute_second = [&num_executed](const BuiltinArguments&) {
-        EXPECT_EQ(1, num_executed++);
-        return Result<void>{};
-    };
-    auto do_execute_third = [&num_executed](const BuiltinArguments&) {
-        EXPECT_EQ(2, num_executed++);
-        return Result<void>{};
-    };
-
-    BuiltinFunctionMap test_function_map = {
-            {"execute_first", {0, 0, {false, do_execute_first}}},
-            {"execute_second", {0, 0, {false, do_execute_second}}},
-            {"execute_third", {0, 0, {false, do_execute_third}}},
-    };
+    TestFunctionMap test_function_map;
+    test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
+    test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
+    test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
@@ -166,7 +124,7 @@
 )init";
 
     ServiceList service_list;
-    TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
+    TestInitText(init_script, TestFunctionMap(), {}, &service_list);
     ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
 
     auto service = service_list.begin()->get();
@@ -222,12 +180,11 @@
     auto execute_command = [&num_executed](const BuiltinArguments& args) {
         EXPECT_EQ(2U, args.size());
         EXPECT_EQ(++num_executed, std::stoi(args[1]));
-        return Result<void>{};
+        return Success();
     };
 
-    BuiltinFunctionMap test_function_map = {
-            {"execute", {1, 1, {false, execute_command}}},
-    };
+    TestFunctionMap test_function_map;
+    test_function_map.Add("execute", 1, 1, false, execute_command);
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
@@ -241,19 +198,3 @@
 
 }  // namespace init
 }  // namespace android
-
-int SubcontextTestChildMain(int, char**);
-int FirmwareTestChildMain(int, char**);
-
-int main(int argc, char** argv) {
-    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
-        return SubcontextTestChildMain(argc, argv);
-    }
-
-    if (argc > 1 && !strcmp(argv[1], "firmware")) {
-        return FirmwareTestChildMain(argc, argv);
-    }
-
-    testing::InitGoogleTest(&argc, argv);
-    return RUN_ALL_TESTS();
-}
diff --git a/init/interface_utils.cpp b/init/interface_utils.cpp
deleted file mode 100644
index 1b76bba..0000000
--- a/init/interface_utils.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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 "interface_utils.h"
-
-#include <fstream>
-#include <sstream>
-
-#include <android-base/strings.h>
-#include <hidl-util/FqInstance.h>
-
-using android::FqInstance;
-using android::FQName;
-using android::base::Error;
-
-namespace android {
-namespace init {
-
-namespace {
-
-std::string FQNamesToString(const std::set<FQName>& fqnames) {
-    std::set<std::string> fqname_strings;
-    for (const FQName& fqname : fqnames) {
-        fqname_strings.insert(fqname.string());
-    }
-    return android::base::Join(fqname_strings, " ");
-}
-
-}  // namespace
-
-Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
-                                                const InterfaceInheritanceHierarchyMap& hierarchy) {
-    std::set<FQName> interface_fqnames;
-    for (const std::string& instance : instances) {
-        // There is insufficient build-time information on AIDL interfaces to check them here
-        // TODO(b/139307527): Rework how services store interfaces to avoid excess string parsing
-        if (base::Split(instance, "/")[0] == "aidl") {
-            continue;
-        }
-
-        FqInstance fqinstance;
-        if (!fqinstance.setTo(instance)) {
-            return Error() << "Unable to parse interface instance '" << instance << "'";
-        }
-        interface_fqnames.insert(fqinstance.getFqName());
-    }
-    return CheckInterfaceInheritanceHierarchy(interface_fqnames, hierarchy);
-}
-
-Result<void> CheckInterfaceInheritanceHierarchy(const std::set<FQName>& interfaces,
-                                                const InterfaceInheritanceHierarchyMap& hierarchy) {
-    std::ostringstream error_stream;
-    for (const FQName& intf : interfaces) {
-        if (hierarchy.count(intf) == 0) {
-            error_stream << "\nInterface is not in the known set of hidl_interfaces: '"
-                         << intf.string()
-                         << "'. Please ensure the interface is spelled correctly and built "
-                         << "by a hidl_interface target.";
-            continue;
-        }
-        const std::set<FQName>& required_interfaces = hierarchy.at(intf);
-        std::set<FQName> diff;
-        std::set_difference(required_interfaces.begin(), required_interfaces.end(),
-                            interfaces.begin(), interfaces.end(),
-                            std::inserter(diff, diff.begin()));
-        if (!diff.empty()) {
-            error_stream << "\nInterface '" << intf.string() << "' requires its full inheritance "
-                         << "hierarchy to be listed in this init_rc file. Missing "
-                         << "interfaces: [" << FQNamesToString(diff) << "]";
-        }
-    }
-    const std::string& errors = error_stream.str();
-    if (!errors.empty()) {
-        return Error() << errors;
-    }
-
-    return {};
-}
-
-std::optional<std::set<FQName>> known_interfaces;
-
-void SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy) {
-    known_interfaces = std::set<FQName>();
-    for (const auto& [intf, inherited_interfaces] : hierarchy) {
-        known_interfaces->insert(intf);
-    }
-}
-
-Result<void> IsKnownInterface(const std::string& instance) {
-    FqInstance fqinstance;
-    if (!fqinstance.setTo(instance)) {
-        return Error() << "Unable to parse interface instance '" << instance << "'";
-    }
-    return IsKnownInterface(fqinstance.getFqName());
-}
-
-Result<void> IsKnownInterface(const FQName& intf) {
-    if (!known_interfaces) {
-        return Error() << "No known interfaces have been loaded.";
-    }
-    if (known_interfaces->count(intf) == 0) {
-        return Error() << "Interface is not in the known set of hidl_interfaces: '" << intf.string()
-                       << "'. Please ensure the interface is spelled correctly and built "
-                       << "by a hidl_interface target.";
-    }
-    return {};
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/interface_utils.h b/init/interface_utils.h
deleted file mode 100644
index 4ca377f..0000000
--- a/init/interface_utils.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 <map>
-#include <set>
-#include <string>
-
-#include <hidl-util/FQName.h>
-
-#include "result.h"
-
-namespace android {
-namespace init {
-
-using InterfaceInheritanceHierarchyMap = std::map<android::FQName, std::set<android::FQName>>;
-
-// For the given set of interfaces / interface instances, checks that each
-// interface's hierarchy of inherited interfaces is also included in the given
-// interface set. Uses the provided hierarchy data.
-Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
-                                                const InterfaceInheritanceHierarchyMap& hierarchy);
-Result<void> CheckInterfaceInheritanceHierarchy(const std::set<android::FQName>& interfaces,
-                                                const InterfaceInheritanceHierarchyMap& hierarchy);
-
-// Saves the set of known interfaces using the provided HIDL interface
-// inheritance hierarchy.
-void SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy);
-
-// Checks if the provided interface is in the set of known interfaces. Returns
-// an empty Result if present, otherwise an Error.
-Result<void> IsKnownInterface(const std::string& instance);
-Result<void> IsKnownInterface(const FQName& intf);
-
-}  // namespace init
-}  // namespace android
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 5f2682b..f5ac44f 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -41,7 +41,7 @@
 
 Keychords::~Keychords() noexcept {
     if (inotify_fd_ >= 0) {
-        epoll_->UnregisterHandler(inotify_fd_);
+        epoll_->UnregisterHandler(inotify_fd_).IgnoreError();
         ::close(inotify_fd_);
     }
     while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);
@@ -212,7 +212,7 @@
     auto it = registration_.find(device);
     if (it == registration_.end()) return;
     auto fd = (*it).second;
-    epoll_->UnregisterHandler(fd);
+    epoll_->UnregisterHandler(fd).IgnoreError();
     registration_.erase(it);
     ::close(fd);
 }
@@ -285,7 +285,7 @@
 
 void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
     epoll_ = epoll;
-    handler_ = std::move(handler);
+    handler_ = handler;
     if (entries_.size()) GeteventOpenDevice();
 }
 
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index 6e9b337..e5a6fd3 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <gtest/gtest.h>
 
@@ -212,11 +213,7 @@
 }
 
 void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
-    auto pending_functions = epoll_.Wait(wait);
-    ASSERT_TRUE(pending_functions) << pending_functions.error();
-    for (const auto& function : *pending_functions) {
-        (*function)();
-    }
+    epoll_.Wait(wait).IgnoreError();
 }
 
 void TestFrame::SetChord(int key, bool value) {
diff --git a/init/keyword_map.h b/init/keyword_map.h
index d92678f..c95fc73 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -14,74 +14,70 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_KEYWORD_MAP_H_
+#define _INIT_KEYWORD_MAP_H_
 
 #include <map>
 #include <string>
-#include <vector>
+
+#include <android-base/stringprintf.h>
 
 #include "result.h"
 
 namespace android {
 namespace init {
 
-// Every init builtin, init service option, and ueventd option has a minimum and maximum number of
-// arguments.  These must be checked both at run time for safety and also at build time for
-// correctness in host_init_verifier.  Instead of copying and pasting the boiler plate code that
-// does this check into each function, it is abstracted in KeywordMap<>.  This class maps keywords
-// to functions and checks that the number of arguments provided falls in the correct range or
-// returns an error otherwise.
-
-// Value is the return value of Find(), which is typically either a single function or a struct with
-// additional information.
-template <typename Value>
+template <typename Function>
 class KeywordMap {
   public:
-    struct MapValue {
-        size_t min_args;
-        size_t max_args;
-        Value value;
-    };
+    using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
+    using Map = std::map<std::string, FunctionInfo>;
 
-    KeywordMap() {}
-    KeywordMap(std::initializer_list<std::pair<const std::string, MapValue>> init) : map_(init) {}
+    virtual ~KeywordMap() {
+    }
 
-    Result<Value> Find(const std::vector<std::string>& args) const {
+    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];
         auto num_args = args.size() - 1;
 
-        auto result_it = map_.find(keyword);
-        if (result_it == map_.end()) {
-            return Errorf("Invalid keyword '{}'", keyword);
+        auto function_info_it = map().find(keyword);
+        if (function_info_it == map().end()) {
+            return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
         }
 
-        auto result = result_it->second;
+        auto function_info = function_info_it->second;
 
-        auto min_args = result.min_args;
-        auto max_args = result.max_args;
+        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 Errorf("{} requires {} argument{}", keyword, min_args,
-                          (min_args > 1 || min_args == 0) ? "s" : "");
+            return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
+                                           (min_args > 1 || min_args == 0) ? "s" : "");
         }
 
         if (num_args < min_args || num_args > max_args) {
             if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
-                return Errorf("{} requires at least {} argument{}", keyword, min_args,
-                              min_args > 1 ? "s" : "");
+                return Error() << StringPrintf("%s requires at least %zu argument%s",
+                                               keyword.c_str(), min_args, min_args > 1 ? "s" : "");
             } else {
-                return Errorf("{} requires between {} and {} arguments", keyword, min_args,
-                              max_args);
+                return Error() << StringPrintf("%s requires between %zu and %zu arguments",
+                                               keyword.c_str(), min_args, max_args);
             }
         }
 
-        return result.value;
+        return std::get<Function>(function_info);
     }
 
   private:
-    std::map<std::string, MapValue> map_;
+    // Map of keyword ->
+    // (minimum number of arguments, maximum number of arguments, function pointer)
+    virtual const Map& map() const = 0;
 };
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/lmkd_service.cpp b/init/lmkd_service.cpp
deleted file mode 100644
index dd1ab4d..0000000
--- a/init/lmkd_service.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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 "lmkd_service.h"
-
-#include <errno.h>
-
-#include <android-base/logging.h>
-#include <liblmkd_utils.h>
-
-#include "service_list.h"
-
-namespace android {
-namespace init {
-
-enum LmkdRegistrationResult {
-    LMKD_REG_SUCCESS,
-    LMKD_CONN_FAILED,
-    LMKD_REG_FAILED,
-};
-
-static int lmkd_socket = -1;
-
-static LmkdRegistrationResult RegisterProcess(uid_t uid, pid_t pid, int oom_score_adjust) {
-    // connect to lmkd if not already connected
-    if (lmkd_socket < 0) {
-        lmkd_socket = lmkd_connect();
-        if (lmkd_socket < 0) {
-            return LMKD_CONN_FAILED;
-        }
-    }
-
-    // register service with lmkd
-    struct lmk_procprio params;
-    params.pid = pid;
-    params.uid = uid;
-    params.oomadj = oom_score_adjust;
-    params.ptype = PROC_TYPE_SERVICE;
-    if (lmkd_register_proc(lmkd_socket, &params) != 0) {
-        // data transfer failed, reset the connection
-        close(lmkd_socket);
-        lmkd_socket = -1;
-        return LMKD_REG_FAILED;
-    }
-
-    return LMKD_REG_SUCCESS;
-}
-
-static bool UnregisterProcess(pid_t pid) {
-    if (lmkd_socket < 0) {
-        // no connection or it was lost, no need to unregister
-        return false;
-    }
-
-    // unregister service
-    struct lmk_procremove params;
-    params.pid = pid;
-    if (lmkd_unregister_proc(lmkd_socket, &params) != 0) {
-        // data transfer failed, reset the connection
-        close(lmkd_socket);
-        lmkd_socket = -1;
-        return false;
-    }
-
-    return true;
-}
-
-static void RegisterServices(pid_t exclude_pid) {
-    for (const auto& service : ServiceList::GetInstance().services()) {
-        auto svc = service.get();
-        if (svc->oom_score_adjust() != DEFAULT_OOM_SCORE_ADJUST) {
-            // skip if process is excluded or not yet forked (pid==0)
-            if (svc->pid() == exclude_pid || svc->pid() == 0) {
-                continue;
-            }
-            if (RegisterProcess(svc->uid(), svc->pid(), svc->oom_score_adjust()) !=
-                LMKD_REG_SUCCESS) {
-                // a failure here resets the connection, will retry during next registration
-                break;
-            }
-        }
-    }
-}
-
-void LmkdRegister(const std::string& name, uid_t uid, pid_t pid, int oom_score_adjust) {
-    bool new_connection = lmkd_socket == -1;
-    LmkdRegistrationResult result;
-
-    result = RegisterProcess(uid, pid, oom_score_adjust);
-    if (result == LMKD_REG_FAILED) {
-        // retry one time if connection to lmkd was lost
-        result = RegisterProcess(uid, pid, oom_score_adjust);
-        new_connection = result == LMKD_REG_SUCCESS;
-    }
-    switch (result) {
-        case LMKD_REG_SUCCESS:
-            // register existing services once new connection is established
-            if (new_connection) {
-                RegisterServices(pid);
-            }
-            break;
-        case LMKD_CONN_FAILED:
-            PLOG(ERROR) << "lmkd connection failed when " << name << " process got started";
-            break;
-        case LMKD_REG_FAILED:
-            PLOG(ERROR) << "lmkd failed to register " << name << " process";
-            break;
-    }
-}
-
-void LmkdUnregister(const std::string& name, pid_t pid) {
-    if (!UnregisterProcess(pid)) {
-        PLOG(ERROR) << "lmkd failed to unregister " << name << " process";
-    }
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/lmkd_service.h b/init/lmkd_service.h
deleted file mode 100644
index 5b51d52..0000000
--- a/init/lmkd_service.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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 <string>
-
-namespace android {
-namespace init {
-
-static const int MIN_OOM_SCORE_ADJUST = -1000;
-static const int MAX_OOM_SCORE_ADJUST = 1000;
-// service with default score is unkillable
-static const int DEFAULT_OOM_SCORE_ADJUST = MIN_OOM_SCORE_ADJUST;
-
-#if defined(__ANDROID__)
-
-void LmkdRegister(const std::string& name, uid_t uid, pid_t pid, int oom_score_adjust);
-void LmkdUnregister(const std::string& name, pid_t pid);
-
-#else  // defined(__ANDROID__)
-
-static inline void LmkdRegister(const std::string&, uid_t, pid_t, int) {}
-static inline void LmkdUnregister(const std::string&, pid_t) {}
-
-#endif  // defined(__ANDROID__)
-
-}  // namespace init
-}  // namespace android
diff --git a/init/main.cpp b/init/main.cpp
index 38bc74b..2ce46ef 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -60,7 +60,7 @@
     if (argc > 1) {
         if (!strcmp(argv[1], "subcontext")) {
             android::base::InitLogging(argv, &android::base::KernelLogger);
-            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
+            const BuiltinFunctionMap function_map;
 
             return SubcontextMain(argc, argv, &function_map);
         }
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index 07b05d8..c61c210 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -16,20 +16,147 @@
 
 #include "modalias_handler.h"
 
+#include <fnmatch.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#include <functional>
 #include <string>
 #include <vector>
 
-#include <modprobe/modprobe.h>
+#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "parser.h"
 
 namespace android {
 namespace init {
 
-ModaliasHandler::ModaliasHandler(const std::vector<std::string>& base_paths)
-    : modprobe_(base_paths) {}
+Result<Success> ModaliasHandler::ParseDepCallback(std::vector<std::string>&& args) {
+    std::vector<std::string> deps;
+
+    // Set first item as our modules path
+    std::string::size_type pos = args[0].find(':');
+    if (pos != std::string::npos) {
+        deps.emplace_back(args[0].substr(0, pos));
+    } else {
+        return Error() << "dependency lines must start with name followed by ':'";
+    }
+
+    // Remaining items are dependencies of our module
+    for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {
+        deps.push_back(*arg);
+    }
+
+    // Key is striped module name to match names in alias file
+    std::size_t start = args[0].find_last_of('/');
+    std::size_t end = args[0].find(".ko:");
+    if ((end - start) <= 1) return Error() << "malformed dependency line";
+    auto mod_name = args[0].substr(start + 1, (end - start) - 1);
+    // module names can have '-', but their file names will have '_'
+    std::replace(mod_name.begin(), mod_name.end(), '-', '_');
+    this->module_deps_[mod_name] = deps;
+
+    return Success();
+}
+
+Result<Success> ModaliasHandler::ParseAliasCallback(std::vector<std::string>&& args) {
+    auto it = args.begin();
+    const std::string& type = *it++;
+
+    if (type != "alias") {
+        return Error() << "we only handle alias lines, got: " << type;
+    }
+
+    if (args.size() != 3) {
+        return Error() << "alias lines must have 3 entries";
+    }
+
+    std::string& alias = *it++;
+    std::string& module_name = *it++;
+    this->module_aliases_.emplace_back(alias, module_name);
+
+    return Success();
+}
+
+ModaliasHandler::ModaliasHandler() {
+    using namespace std::placeholders;
+
+    static const std::string base_paths[] = {
+            "/vendor/lib/modules/",
+            "/lib/modules/",
+            "/odm/lib/modules/",
+    };
+
+    Parser alias_parser;
+    auto alias_callback = std::bind(&ModaliasHandler::ParseAliasCallback, this, _1);
+    alias_parser.AddSingleLineParser("alias", alias_callback);
+    for (const auto& base_path : base_paths) alias_parser.ParseConfig(base_path + "modules.alias");
+
+    Parser dep_parser;
+    auto dep_callback = std::bind(&ModaliasHandler::ParseDepCallback, this, _1);
+    dep_parser.AddSingleLineParser("", dep_callback);
+    for (const auto& base_path : base_paths) dep_parser.ParseConfig(base_path + "modules.dep");
+}
+
+Result<Success> ModaliasHandler::Insmod(const std::string& path_name, const std::string& args) {
+    base::unique_fd fd(
+            TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+    if (fd == -1) return ErrnoError() << "Could not open module '" << path_name << "'";
+
+    int ret = syscall(__NR_finit_module, fd.get(), args.c_str(), 0);
+    if (ret != 0) {
+        if (errno == EEXIST) {
+            // Module already loaded
+            return Success();
+        }
+        return ErrnoError() << "Failed to insmod '" << path_name << "' with args '" << args << "'";
+    }
+
+    LOG(INFO) << "Loaded kernel module " << path_name;
+    return Success();
+}
+
+Result<Success> ModaliasHandler::InsmodWithDeps(const std::string& module_name,
+                                                const std::string& args) {
+    if (module_name.empty()) {
+        return Error() << "Need valid module name";
+    }
+
+    auto it = module_deps_.find(module_name);
+    if (it == module_deps_.end()) {
+        return Error() << "Module '" << module_name << "' not in dependency file";
+    }
+    auto& dependencies = it->second;
+
+    // load module dependencies in reverse order
+    for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
+        if (auto result = Insmod(*dep, ""); !result) return result;
+    }
+
+    // load target module itself with args
+    return Insmod(dependencies[0], args);
+}
 
 void ModaliasHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.modalias.empty()) return;
-    modprobe_.LoadWithAliases(uevent.modalias, true);
+
+    for (const auto& [alias, module] : module_aliases_) {
+        if (fnmatch(alias.c_str(), uevent.modalias.c_str(), 0) != 0) continue;  // Keep looking
+
+        LOG(DEBUG) << "Loading kernel module '" << module << "' for alias '" << uevent.modalias
+                   << "'";
+
+        if (auto result = InsmodWithDeps(module, ""); !result) {
+            LOG(ERROR) << "Cannot load module: " << result.error();
+            // try another one since there may be another match
+            continue;
+        }
+
+        // loading was successful
+        return;
+    }
 }
 
 }  // namespace init
diff --git a/init/modalias_handler.h b/init/modalias_handler.h
index ce89a05..3247c86 100644
--- a/init/modalias_handler.h
+++ b/init/modalias_handler.h
@@ -17,10 +17,10 @@
 #pragma once
 
 #include <string>
+#include <unordered_map>
 #include <vector>
 
-#include <modprobe/modprobe.h>
-
+#include "result.h"
 #include "uevent.h"
 #include "uevent_handler.h"
 
@@ -29,13 +29,20 @@
 
 class ModaliasHandler : public UeventHandler {
   public:
-    ModaliasHandler(const std::vector<std::string>&);
+    ModaliasHandler();
     virtual ~ModaliasHandler() = default;
 
     void HandleUevent(const Uevent& uevent) override;
 
   private:
-    Modprobe modprobe_;
+    Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args);
+    Result<Success> Insmod(const std::string& path_name, const std::string& args);
+
+    Result<Success> ParseDepCallback(std::vector<std::string>&& args);
+    Result<Success> ParseAliasCallback(std::vector<std::string>&& args);
+
+    std::vector<std::pair<std::string, std::string>> module_aliases_;
+    std::unordered_map<std::string, std::vector<std::string>> module_deps_;
 };
 
 }  // namespace init
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index 791a019..c8f0e76 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -121,7 +121,7 @@
 }
 
 MountHandler::~MountHandler() {
-    if (fp_) epoll_->UnregisterHandler(fileno(fp_.get()));
+    if (fp_) epoll_->UnregisterHandler(fileno(fp_.get())).IgnoreError();
 }
 
 void MountHandler::MountHandlerFunction() {
@@ -140,11 +140,11 @@
         }
     }
     free(buf);
-    for (auto& entry : untouched) {
+    for (auto entry : untouched) {
         SetMountProperty(entry, false);
         mounts_.erase(entry);
     }
-    for (auto& entry : touched) {
+    for (auto entry : touched) {
         SetMountProperty(entry, true);
         mounts_.emplace(std::move(entry));
     }
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 0745148..5305dc7 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -79,38 +79,6 @@
     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 ART APEX
-    constexpr const char kArtApexMountPath[] = "/system/apex/com.android.art";
-    static const std::vector<std::string> kArtApexDirNames = {"com.android.art.release",
-                                                              "com.android.art.debug"};
-    bool success = false;
-    for (const auto& name : kArtApexDirNames) {
-        std::string path = std::string(kSystemApex) + "/" + name;
-        if (access(path.c_str(), F_OK) == 0) {
-            if (mount(path.c_str(), kArtApexMountPath, nullptr, MS_BIND, nullptr) == 0) {
-                success = true;
-                break;
-            }
-        }
-    }
-    if (!success) {
-        PLOG(ERROR) << "Failed to bind mount the ART APEX to " << kArtApexMountPath;
-    }
-    return success;
-}
-
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -161,8 +129,6 @@
         default_ns_id = GetMountNamespaceId();
     }
 
-    success &= ActivateFlattenedApexesIfPossible();
-
     LOG(INFO) << "SetupMountNamespaces done";
     return success;
 }
diff --git a/init/parser.cpp b/init/parser.cpp
index 6ab61cb..bbfbdc6 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -37,7 +37,7 @@
 }
 
 void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
-    line_callbacks_.emplace_back(prefix, std::move(callback));
+    line_callbacks_.emplace_back(prefix, callback);
 }
 
 void Parser::ParseData(const std::string& filename, std::string* data) {
diff --git a/init/parser.h b/init/parser.h
index 95b0cd7..f30bda7 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<void> indicating if they have an error. It will be reported along
+//  functions return Result<Success> indicating if they have an error. It will be reported along
 //  with the filename and line number of where the error occurred.
 //
 //  1) ParseSection
@@ -51,10 +51,10 @@
 class SectionParser {
   public:
     virtual ~SectionParser() {}
-    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 Result<Success> ParseSection(std::vector<std::string>&& args,
+                                         const std::string& filename, int line) = 0;
+    virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
+    virtual Result<Success> EndSection() { return Success(); };
     virtual void EndFile(){};
 };
 
@@ -67,7 +67,7 @@
     //  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<void>(std::vector<std::string>&&)>;
+    using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>;
 
     Parser();
 
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index baa9ad4..21adce9 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 | O_CLOEXEC));
+        unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
         if (fd == -1) {
             PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
             continue;
@@ -169,7 +169,7 @@
     return Error() << "Unable to parse persistent property file: Could not parse protobuf";
 }
 
-Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
+Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
     const std::string temp_filename = persistent_property_filename + ".tmp";
     unique_fd fd(TEMP_FAILURE_RETRY(
         open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
@@ -191,7 +191,7 @@
         unlink(temp_filename.c_str());
         return Error(saved_errno) << "Unable to rename persistent property file";
     }
-    return {};
+    return Success();
 }
 
 // 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 3845a0d..5f4df85 100644
--- a/init/persistent_properties.h
+++ b/init/persistent_properties.h
@@ -30,7 +30,7 @@
 
 // Exposed only for testing
 Result<PersistentProperties> LoadPersistentPropertyFile();
-Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
+Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
 extern std::string persistent_property_filename;
 
 }  // namespace init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3baaf7c..f2c7462 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -41,10 +41,7 @@
 
 #include <map>
 #include <memory>
-#include <mutex>
-#include <optional>
 #include <queue>
-#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -64,10 +61,8 @@
 #include "init.h"
 #include "persistent_properties.h"
 #include "property_type.h"
-#include "proto_utils.h"
 #include "selinux.h"
 #include "subcontext.h"
-#include "system/core/init/property_service.pb.h"
 #include "util.h"
 
 using namespace std::literals;
@@ -79,7 +74,6 @@
 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;
@@ -92,11 +86,13 @@
 static bool persistent_properties_loaded = false;
 
 static int property_set_fd = -1;
-static int init_socket = -1;
-static bool accept_messages = false;
 
 static PropertyInfoAreaFile property_info_area;
 
+uint32_t InitPropertySet(const std::string& name, const std::string& value);
+
+uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
+
 void CreateSerializedPropertyInfo();
 
 struct PropertyAuditData {
@@ -104,24 +100,7 @@
     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()) {
@@ -164,17 +143,6 @@
     return has_access;
 }
 
-static void SendPropertyChanged(const std::string& name, const std::string& value) {
-    auto property_msg = PropertyMessage{};
-    auto* changed_message = property_msg.mutable_changed_message();
-    changed_message->set_name(name);
-    changed_message->set_value(value);
-
-    if (auto result = SendMessage(init_socket, property_msg); !result) {
-        LOG(ERROR) << "Failed to send property changed message: " << result.error();
-    }
-}
-
 static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
     size_t valuelen = value.size();
 
@@ -183,8 +151,13 @@
         return PROP_ERROR_INVALID_NAME;
     }
 
-    if (auto result = IsLegalPropertyValue(name, value); !result) {
-        *error = result.error().message();
+    if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
+        *error = "Property value too long";
+        return PROP_ERROR_INVALID_VALUE;
+    }
+
+    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+        *error = "Value is not a UTF8 encoded string";
         return PROP_ERROR_INVALID_VALUE;
     }
 
@@ -210,54 +183,112 @@
     if (persistent_properties_loaded && StartsWith(name, "persist.")) {
         WritePersistentProperty(name, value);
     }
-    // If init hasn't started its main loop, then it won't be handling property changed messages
-    // anyway, so there's no need to try to send them.
-    if (accept_messages) {
-        SendPropertyChanged(name, value);
+    property_changed(name, value);
+    return PROP_SUCCESS;
+}
+
+typedef int (*PropertyAsyncFunc)(const std::string&, const std::string&);
+
+struct PropertyChildInfo {
+    pid_t pid;
+    PropertyAsyncFunc func;
+    std::string name;
+    std::string value;
+};
+
+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;
 }
 
-class AsyncRestorecon {
-  public:
-    void TriggerRestorecon(const std::string& path) {
-        auto guard = std::lock_guard{mutex_};
-        paths_.emplace(path);
+static int RestoreconRecursiveAsync(const std::string& name, const std::string& value) {
+    return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+}
 
-        if (!thread_started_) {
-            thread_started_ = true;
-            std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();
-        }
+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";
+        return PROP_ERROR_INVALID_NAME;
     }
 
-  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;
+    uint32_t result = 0;
+    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+    std::string error;
+    result = HandlePropertySet(name, value, kInitContext.c_str(), cr, &error);
+    if (result != PROP_SUCCESS) {
+        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
     }
 
-    std::mutex mutex_;
-    std::queue<std::string> paths_;
-    bool thread_started_ = false;
-};
+    return result;
+}
 
 class SocketConnection {
   public:
     SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
 
+    ~SocketConnection() { close(socket_); }
+
     bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
         return RecvFully(value, sizeof(*value), timeout_ms);
     }
@@ -294,27 +325,22 @@
     }
 
     bool SendUint32(uint32_t value) {
-        if (!socket_.ok()) {
-            return true;
-        }
         int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
         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;
-    }
-
-    [[nodiscard]] int Release() { return socket_.release(); }
+    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];
@@ -382,46 +408,12 @@
         return bytes_left == 0;
     }
 
-    unique_fd socket_;
+    int socket_;
     ucred cred_;
 
     DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
 };
 
-static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
-                                   SocketConnection* socket, std::string* error) {
-    if (!accept_messages) {
-        *error = "Received control message after shutdown, ignoring";
-        return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
-    }
-
-    auto property_msg = PropertyMessage{};
-    auto* control_message = property_msg.mutable_control_message();
-    control_message->set_msg(msg);
-    control_message->set_name(name);
-    control_message->set_pid(pid);
-
-    // We must release the fd before sending it to init, otherwise there will be a race with init.
-    // If init calls close() before Release(), then fdsan will see the wrong tag and abort().
-    int fd = -1;
-    if (socket != nullptr && SelinuxGetVendorAndroidVersion() > __ANDROID_API_Q__) {
-        fd = socket->Release();
-        control_message->set_fd(fd);
-    }
-
-    if (auto result = SendMessage(init_socket, property_msg); !result) {
-        // We've already released the fd above, so if we fail to send the message to init, we need
-        // to manually free it here.
-        if (fd != -1) {
-            close(fd);
-        }
-        *error = "Failed to send control message: " + result.error().message();
-        return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
-    }
-
-    return PROP_SUCCESS;
-}
-
 bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
                                const std::string& source_context, const ucred& cr) {
     // We check the legacy method first but these properties are dontaudit, so we only log an audit
@@ -489,14 +481,14 @@
 
 // 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,
-                           SocketConnection* socket, std::string* error) {
+                           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 SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
+        HandleControlMessage(name.c_str() + 4, value, cr.pid);
+        return PROP_SUCCESS;
     }
 
     // sys.powerctl is a special property that is used to make the device reboot.  We want to log
@@ -514,33 +506,13 @@
                   << process_log_string;
     }
 
-    // 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;
+    if (name == "selinux.restorecon_recursive") {
+        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
     }
 
     return PropertySet(name, value, error);
 }
 
-uint32_t InitPropertySet(const std::string& name, const std::string& value) {
-    uint32_t result = 0;
-    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
-    std::string error;
-    result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error);
-    if (result != PROP_SUCCESS) {
-        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
-    }
-
-    return result;
-}
-
-uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
-
 static void handle_property_set_fd() {
     static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
 
@@ -581,19 +553,14 @@
         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, source_context, cr, nullptr, &error);
+            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
         if (result != PROP_SUCCESS) {
-            LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
-                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
+            LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
+                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+                       << error;
         }
 
         break;
@@ -609,19 +576,13 @@
           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, source_context, cr, &socket, &error);
+        uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
         if (result != PROP_SUCCESS) {
-            LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
-                       << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
+            LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
+                       << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+                       << error;
         }
         socket.SendUint32(result);
         break;
@@ -646,16 +607,11 @@
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
-    static constexpr const char* const kVendorPathPrefixes[2] = {
-            "/vendor",
-            "/odm",
-    };
-
-    const char* context = kInitContext;
+    const char* context = kInitContext.c_str();
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        for (const auto& vendor_path_prefix : kVendorPathPrefixes) {
-            if (StartsWith(filename, vendor_path_prefix)) {
-                context = kVendorContext;
+        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+            if (StartsWith(filename, path_prefix)) {
+                context = secontext;
             }
         }
     }
@@ -687,14 +643,13 @@
             }
 
             std::string raw_filename(fn);
-            auto expanded_filename = ExpandProps(raw_filename);
-
-            if (!expanded_filename) {
-                LOG(ERROR) << "Could not expand filename ': " << expanded_filename.error();
+            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);
+            load_properties_from_file(expanded_filename.c_str(), key, properties);
         } else {
             value = strchr(key, '=');
             if (!value) continue;
@@ -707,14 +662,14 @@
 
             if (flen > 0) {
                 if (filter[flen - 1] == '*') {
-                    if (strncmp(key, filter, flen - 1) != 0) continue;
+                    if (strncmp(key, filter, flen - 1)) continue;
                 } else {
-                    if (strcmp(key, filter) != 0) continue;
+                    if (strcmp(key, filter)) continue;
                 }
             }
 
             if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
-                std::string{key} == kRestoreconProperty) {
+                key == "selinux.restorecon_recursive"s) {
                 LOG(ERROR) << "Ignoring disallowed property '" << key
                            << "' with special meaning in prop file '" << filename << "'";
                 continue;
@@ -787,6 +742,33 @@
     }
 }
 
+/* When booting an encrypted system, /data is not mounted when the
+ * property service is started, so any properties stored there are
+ * not loaded.  Vold triggers init to load these properties once it
+ * has mounted /data.
+ */
+void load_persist_props(void) {
+    // Devices with FDE have load_persist_props called twice; the first time when the temporary
+    // /data partition is mounted and then again once /data is truly mounted.  We do not want to
+    // read persistent properties from the temporary /data partition or mark persistent properties
+    // as having been loaded during the first call, so we return in that case.
+    std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+    std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+    if (crypto_state == "encrypted" && crypto_type == "block") {
+        static size_t num_calls = 0;
+        if (++num_calls == 1) return;
+    }
+
+    load_override_properties();
+    /* Read persistent properties after all default values have been loaded. */
+    auto persistent_properties = LoadPersistentProperties();
+    for (const auto& persistent_property_record : persistent_properties.properties()) {
+        property_set(persistent_property_record.name(), persistent_property_record.value());
+    }
+    persistent_properties_loaded = true;
+    property_set("ro.persistent_properties.ready", "true");
+}
+
 // 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() {
@@ -795,9 +777,10 @@
             "brand", "device", "manufacturer", "model", "name",
     };
     const char* RO_PRODUCT_PROPS_ALLOWED_SOURCES[] = {
-            "odm", "product", "system_ext", "system", "vendor",
+            "odm", "product", "product_services", "system", "vendor",
     };
-    const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = "product,odm,vendor,system_ext,system";
+    const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER =
+            "product,product_services,odm,vendor,system";
     const std::string EMPTY = "";
 
     std::string ro_product_props_source_order =
@@ -904,7 +887,6 @@
         }
     }
     load_properties_from_file("/system/build.prop", nullptr, &properties);
-    load_properties_from_file("/system_ext/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__) {
@@ -914,6 +896,7 @@
         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) {
@@ -935,6 +918,19 @@
     update_sys_usb_config();
 }
 
+static int SelinuxAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
+    auto* d = reinterpret_cast<PropertyAuditData*>(data);
+
+    if (!d || !d->name || !d->cr) {
+        LOG(ERROR) << "AuditCallback invoked with null data arguments!";
+        return 0;
+    }
+
+    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
+             d->cr->gid);
+    return 0;
+}
+
 bool LoadPropertyInfoFromFile(const std::string& filename,
                               std::vector<PropertyInfoEntry>* property_infos) {
     auto file_contents = std::string();
@@ -964,10 +960,6 @@
         // Don't check for failure here, so we always have a sane list of properties.
         // E.g. In case of recovery, the vendor partition will not have mounted and we
         // still need the system / platform properties to function.
-        if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
-            LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
-                                     &property_infos);
-        }
         if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
                                       &property_infos)) {
             // Fallback to nonplat_* if vendor_* doesn't exist.
@@ -985,7 +977,6 @@
         if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
             return;
         }
-        LoadPropertyInfoFromFile("/system_ext_property_contexts", &property_infos);
         if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {
             // Fallback to nonplat_* if vendor_* doesn't exist.
             LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
@@ -1009,97 +1000,24 @@
     selinux_android_restorecon(kPropertyInfosPath, 0);
 }
 
-static void HandleInitSocket() {
-    auto message = ReadMessage(init_socket);
-    if (!message) {
-        LOG(ERROR) << "Could not read message from init_dedicated_recv_socket: " << message.error();
-        return;
-    }
+void StartPropertyService(Epoll* epoll) {
+    selinux_callback cb;
+    cb.func_audit = SelinuxAuditCallback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
-    auto init_message = InitMessage{};
-    if (!init_message.ParseFromString(*message)) {
-        LOG(ERROR) << "Could not parse message from init";
-        return;
-    }
-
-    switch (init_message.msg_case()) {
-        case InitMessage::kLoadPersistentProperties: {
-            load_override_properties();
-            // Read persistent properties after all default values have been loaded.
-            auto persistent_properties = LoadPersistentProperties();
-            for (const auto& persistent_property_record : persistent_properties.properties()) {
-                InitPropertySet(persistent_property_record.name(),
-                                persistent_property_record.value());
-            }
-            InitPropertySet("ro.persistent_properties.ready", "true");
-            persistent_properties_loaded = true;
-            break;
-        }
-        case InitMessage::kStopSendingMessages: {
-            accept_messages = false;
-            break;
-        }
-        case InitMessage::kStartSendingMessages: {
-            accept_messages = true;
-            break;
-        }
-        default:
-            LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
-    }
-}
-
-static void PropertyServiceThread() {
-    Epoll epoll;
-    if (auto result = epoll.Open(); !result) {
-        LOG(FATAL) << result.error();
-    }
-
-    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
-        LOG(FATAL) << result.error();
-    }
-
-    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result) {
-        LOG(FATAL) << result.error();
-    }
-
-    while (true) {
-        auto pending_functions = epoll.Wait(std::nullopt);
-        if (!pending_functions) {
-            LOG(ERROR) << pending_functions.error();
-        } else {
-            for (const auto& function : *pending_functions) {
-                (*function)();
-            }
-        }
-    }
-}
-
-void StartPropertyService(int* epoll_socket) {
     property_set("ro.property_service.version", "2");
 
-    int sockets[2];
-    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
-        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
-    }
-    *epoll_socket = sockets[0];
-    init_socket = sockets[1];
-    accept_messages = true;
-
-    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, {})) {
-        property_set_fd = *result;
-    } else {
-        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                                   false, 0666, 0, 0, nullptr);
+    if (property_set_fd == -1) {
+        PLOG(FATAL) << "start_property_service socket creation failed";
     }
 
     listen(property_set_fd, 8);
 
-    std::thread{PropertyServiceThread}.detach();
-
-    property_set = [](const std::string& key, const std::string& value) -> uint32_t {
-        android::base::SetProperty(key, value);
-        return 0;
-    };
+    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
+        PLOG(FATAL) << result.error();
+    }
 }
 
 }  // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 8f7d8d9..85e7bc0 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_PROPERTY_H
+#define _INIT_PROPERTY_H
 
 #include <sys/socket.h>
 
@@ -25,15 +26,22 @@
 namespace android {
 namespace init {
 
-static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
-
 bool CanReadProperty(const std::string& source_context, const std::string& name);
 
 extern uint32_t (*property_set)(const std::string& name, const std::string& value);
 
-void property_init();
-void property_load_boot_defaults(bool load_debug_prop);
-void StartPropertyService(int* epoll_socket);
+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);
+void property_load_boot_defaults(bool);
+void load_persist_props(void);
+void load_system_props(void);
+void StartPropertyService(Epoll* epoll);
 
 }  // namespace init
 }  // namespace android
+
+#endif  /* _INIT_PROPERTY_H */
diff --git a/init/property_service.proto b/init/property_service.proto
deleted file mode 100644
index 08268d9..0000000
--- a/init/property_service.proto
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 = "proto2";
-option optimize_for = LITE_RUNTIME;
-
-message PropertyMessage {
-    message ControlMessage {
-        optional string msg = 1;
-        optional string name = 2;
-        optional int32 pid = 3;
-        optional int32 fd = 4;
-    }
-
-    message ChangedMessage {
-        optional string name = 1;
-        optional string value = 2;
-    }
-
-    oneof msg {
-        ControlMessage control_message = 1;
-        ChangedMessage changed_message = 2;
-    };
-}
-
-message InitMessage {
-    oneof msg {
-        bool load_persistent_properties = 1;
-        bool stop_sending_messages = 2;
-        bool start_sending_messages = 3;
-    };
-}
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 0f4cd0d..c038aff 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -56,11 +56,6 @@
 }
 
 TEST(property_service, non_utf8_value) {
-    if (getuid() != 0) {
-        GTEST_SKIP() << "Skipping test, must be run as root.";
-        return;
-    }
-
     ASSERT_TRUE(SetProperty("property_service_utf8_test", "base_success"));
     EXPECT_FALSE(SetProperty("property_service_utf8_test", "\x80"));
     EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xC2\x01"));
diff --git a/init/proto_utils.h b/init/proto_utils.h
deleted file mode 100644
index 93a7d57..0000000
--- a/init/proto_utils.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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/socket.h>
-#include <unistd.h>
-
-#include <string>
-
-#include "result.h"
-
-namespace android {
-namespace init {
-
-constexpr size_t kBufferSize = 4096;
-
-inline Result<std::string> ReadMessage(int socket) {
-    char buffer[kBufferSize] = {};
-    auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
-    if (result == 0) {
-        return Error();
-    } else if (result < 0) {
-        return ErrnoError();
-    }
-    return std::string(buffer, result);
-}
-
-template <typename T>
-Result<void> SendMessage(int socket, const T& message) {
-    std::string message_string;
-    if (!message.SerializeToString(&message_string)) {
-        return Error() << "Unable to serialize message";
-    }
-
-    if (message_string.size() > kBufferSize) {
-        return Error() << "Serialized message too long to send";
-    }
-
-    if (auto result =
-                TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
-        result != static_cast<long>(message_string.size())) {
-        return ErrnoError() << "send() failed to send message contents";
-    }
-    return {};
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 4a16969..5b90969 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -19,20 +19,17 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <linux/fs.h>
-#include <linux/loop.h>
 #include <mntent.h>
-#include <semaphore.h>
-#include <stdlib.h>
+#include <linux/loop.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
-#include <sys/stat.h>
 #include <sys/swap.h>
+#include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 
-#include <chrono>
 #include <memory>
 #include <set>
 #include <thread>
@@ -43,7 +40,7 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/properties.h>
-#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
@@ -53,46 +50,22 @@
 #include <private/android_filesystem_config.h>
 #include <selinux/selinux.h>
 
-#include "action.h"
 #include "action_manager.h"
-#include "builtin_arguments.h"
 #include "init.h"
-#include "mount_namespace.h"
 #include "property_service.h"
 #include "reboot_utils.h"
 #include "service.h"
-#include "service_list.h"
 #include "sigchld_handler.h"
-#include "util.h"
-
-#define PROC_SYSRQ "/proc/sysrq-trigger"
-
-using namespace std::literals;
 
 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 {
 
-static bool shutting_down = false;
-
-static const std::set<std::string> kDebuggingServices{"tombstoned", "logd", "adbd", "console"};
-
-static std::vector<Service*> GetDebuggingServices(bool only_post_data) {
-    std::vector<Service*> ret;
-    ret.reserve(kDebuggingServices.size());
-    for (const auto& s : ServiceList::GetInstance()) {
-        if (kDebuggingServices.count(s->name()) && (!only_post_data || s->is_post_data())) {
-            ret.push_back(s.get());
-        }
-    }
-    return ret;
-}
-
 // represents umount status during reboot / shutdown.
 enum UmountStat {
     /* umount succeeded. */
@@ -138,16 +111,16 @@
                     "-a",
                     mnt_fsname_.c_str(),
             };
-            logwrap_fork_execvp(arraysize(f2fs_argv), f2fs_argv, &st, false, LOG_KLOG, true,
-                                nullptr);
+            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",
                     "-y",
                     mnt_fsname_.c_str(),
             };
-            logwrap_fork_execvp(arraysize(ext4_argv), ext4_argv, &st, false, LOG_KLOG, true,
-                                nullptr);
+            android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG,
+                                    true, nullptr, nullptr, 0);
         }
     }
 
@@ -182,17 +155,11 @@
     }
 }
 
-static Result<void> ShutdownVold() {
+static void ShutdownVold() {
     const char* vdc_argv[] = {"/system/bin/vdc", "volume", "shutdown"};
     int status;
-    if (logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true,
-                            nullptr) != 0) {
-        return ErrnoError() << "Failed to call 'vdc volume shutdown'";
-    }
-    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
-        return {};
-    }
-    return Error() << "'vdc volume shutdown' failed : " << status;
+    android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true, LOG_KLOG, true,
+                            nullptr, nullptr, 0);
 }
 
 static void LogShutdownTime(UmountStat stat, Timer* t) {
@@ -200,25 +167,11 @@
                  << stat;
 }
 
-static bool IsDataMounted() {
-    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;
-    }
-    mntent* mentry;
-    while ((mentry = getmntent(fp.get())) != nullptr) {
-        if (mentry->mnt_dir == "/data"s) {
-            return true;
-        }
-    }
-    return false;
-}
-
-// Find all read+write block devices and emulated devices in /proc/mounts and add them to
-// the correpsponding list.
-static bool FindPartitionsToUmount(std::vector<MountEntry>* block_dev_partitions,
-                                   std::vector<MountEntry>* emulated_partitions, bool dump) {
+/* 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", "re"), endmntent);
     if (fp == nullptr) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
@@ -235,10 +188,10 @@
             // Do not umount them as shutdown critical services may rely on them.
             if (mount_dir != "/" && mount_dir != "/system" && mount_dir != "/vendor" &&
                 mount_dir != "/oem") {
-                block_dev_partitions->emplace(block_dev_partitions->begin(), *mentry);
+                blockDevPartitions->emplace(blockDevPartitions->begin(), *mentry);
             }
         } else if (MountEntry::IsEmulatedDevice(*mentry)) {
-            emulated_partitions->emplace(emulated_partitions->begin(), *mentry);
+            emulatedPartitions->emplace(emulatedPartitions->begin(), *mentry);
         }
     }
     return true;
@@ -249,13 +202,13 @@
     if (!security_getenforce()) {
         LOG(INFO) << "Run lsof";
         const char* lsof_argv[] = {"/system/bin/lsof"};
-        logwrap_fork_execvp(arraysize(lsof_argv), lsof_argv, &status, false, LOG_KLOG, true,
-                            nullptr);
+        android_fork_execvp_ext(arraysize(lsof_argv), (char**)lsof_argv, &status, true, LOG_KLOG,
+                                true, nullptr, nullptr, 0);
     }
     FindPartitionsToUmount(nullptr, nullptr, true);
     // dump current CPU stack traces and uninterruptible tasks
-    WriteStringToFile("l", PROC_SYSRQ);
-    WriteStringToFile("w", PROC_SYSRQ);
+    android::base::WriteStringToFile("l", "/proc/sysrq-trigger");
+    android::base::WriteStringToFile("w", "/proc/sysrq-trigger");
 }
 
 static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
@@ -295,95 +248,7 @@
     }
 }
 
-static void KillAllProcesses() {
-    WriteStringToFile("i", PROC_SYSRQ);
-}
-
-// Create reboot/shutdwon monitor thread
-void RebootMonitorThread(unsigned int cmd, const std::string& reboot_target,
-                         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) {
-                if (false) {
-                    // SEPolicy will block debuggerd from running and this is intentional.
-                    // But these lines are left to be enabled during debugging.
-                    LOG(INFO) << "Try to dump init process call trace:";
-                    const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
-                    int status;
-                    logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG,
-                                        true, nullptr);
-                }
-                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, reboot_target);
-            }
-
-            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;
-        }
-    }
-}
+static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
 
 /* Try umounting all emulated file systems R/W block device cfile systems.
  * This will just try umount and give it up if it fails.
@@ -394,13 +259,12 @@
  *
  * return true when umount was successful. false when timed out.
  */
-static UmountStat TryUmountAndFsck(unsigned int cmd, bool run_fsck,
-                                   std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {
+static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
     Timer t;
     std::vector<MountEntry> block_devices;
     std::vector<MountEntry> emulated_devices;
 
-    if (run_fsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
+    if (runFsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
         return UMOUNT_STAT_ERROR;
     }
 
@@ -414,18 +278,12 @@
         if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
     }
 
-    if (stat == UMOUNT_STAT_SUCCESS && run_fsck) {
-        LOG(INFO) << "Pause reboot monitor thread before fsck";
-        sem_post(reboot_semaphore);
-
+    if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
         // 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;
 }
@@ -435,11 +293,11 @@
 #define ZRAM_DEVICE   "/dev/block/zram0"
 #define ZRAM_RESET    "/sys/block/zram0/reset"
 #define ZRAM_BACK_DEV "/sys/block/zram0/backing_dev"
-static Result<void> KillZramBackingDevice() {
+static void KillZramBackingDevice() {
     std::string backing_dev;
-    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return {};
+    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) return;
 
-    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return {};
+    if (!android::base::StartsWith(backing_dev, "/dev/block/loop")) return;
 
     // cut the last "\n"
     backing_dev.erase(backing_dev.length() - 1);
@@ -448,92 +306,41 @@
     Timer swap_timer;
     LOG(INFO) << "swapoff() start...";
     if (swapoff(ZRAM_DEVICE) == -1) {
-        return ErrnoError() << "zram_backing_dev: swapoff (" << backing_dev << ")"
-                            << " failed";
+        LOG(ERROR) << "zram_backing_dev: swapoff (" << backing_dev << ")" << " failed";
+        return;
     }
     LOG(INFO) << "swapoff() took " << swap_timer;;
 
-    if (!WriteStringToFile("1", ZRAM_RESET)) {
-        return Error() << "zram_backing_dev: reset (" << backing_dev << ")"
-                       << " failed";
+    if (!android::base::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) {
-        return ErrnoError() << "zram_backing_dev: open(" << backing_dev << ")"
-                            << " failed";
+        LOG(ERROR) << "zram_backing_dev: open(" << backing_dev << ")" << " failed";
+        return;
     }
 
     if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) {
-        return ErrnoError() << "zram_backing_dev: loop_clear (" << backing_dev << ")"
-                            << " failed";
+        LOG(ERROR) << "zram_backing_dev: loop_clear (" << backing_dev << ")" << " failed";
+        return;
     }
     LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully.";
-    return {};
-}
-
-// Stops given services, waits for them to be stopped for |timeout| ms.
-// If terminate is true, then SIGTERM is sent to services, otherwise SIGKILL is sent.
-static void StopServices(const std::vector<Service*>& services, std::chrono::milliseconds timeout,
-                         bool terminate) {
-    LOG(INFO) << "Stopping " << services.size() << " services by sending "
-              << (terminate ? "SIGTERM" : "SIGKILL");
-    std::vector<pid_t> pids;
-    pids.reserve(services.size());
-    for (const auto& s : services) {
-        if (s->pid() > 0) {
-            pids.push_back(s->pid());
-        }
-        if (terminate) {
-            s->Terminate();
-        } else {
-            s->Stop();
-        }
-    }
-    if (timeout > 0ms) {
-        WaitToBeReaped(pids, timeout);
-    } else {
-        // Even if we don't to wait for services to stop, we still optimistically reap zombies.
-        ReapAnyOutstandingChildren();
-    }
-}
-
-// Like StopServices, but also logs all the services that failed to stop after the provided timeout.
-// Returns number of violators.
-static int StopServicesAndLogViolations(const std::vector<Service*>& services,
-                                        std::chrono::milliseconds timeout, bool terminate) {
-    StopServices(services, timeout, terminate);
-    int still_running = 0;
-    for (const auto& s : services) {
-        if (s->IsRunning()) {
-            LOG(ERROR) << "[service-misbehaving] : service '" << s->name() << "' is still running "
-                       << timeout.count() << "ms after receiving "
-                       << (terminate ? "SIGTERM" : "SIGKILL");
-            still_running++;
-        }
-    }
-    return still_running;
 }
 
 //* Reboot / shutdown the system.
 // cmd ANDROID_RB_* as defined in android_reboot.h
 // reason Reason string like "reboot", "shutdown,userrequested"
-// reboot_target Reboot target string like "bootloader". Otherwise, it should be an empty string.
-// run_fsck Whether to run fsck after umount is done.
+// 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& reboot_target,
-                     bool run_fsck) {
+static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
+                     bool runFsck) {
     Timer t;
-    LOG(INFO) << "Reboot start, reason: " << reason << ", reboot_target: " << reboot_target;
-
-    // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
-    // worry about unmounting it.
-    if (!IsDataMounted()) {
-        sync();
-        RebootSystem(cmd, reboot_target);
-        abort();
-    }
+    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
 
     // Ensure last reboot reason is reduced to canonical
     // alias reported in bootloader or system boot reason.
@@ -562,30 +369,12 @@
     }
     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, reboot_target);
-    }
-
-    // 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, reboot_target, &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.
     const std::set<std::string> to_starts{"watchdogd"};
-    std::vector<Service*> stop_first;
-    stop_first.reserve(ServiceList::GetInstance().services().size());
     for (const auto& s : ServiceList::GetInstance()) {
-        if (kDebuggingServices.count(s->name())) {
-            // keep debugging tools until non critical ones are all gone.
+        if (kill_after_apps.count(s->name())) {
             s->SetShutdownCritical();
         } else if (to_starts.count(s->name())) {
             if (auto result = s->Start(); !result) {
@@ -599,8 +388,6 @@
                 LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
                            << "': " << result.error();
             }
-        } else {
-            stop_first.push_back(s.get());
         }
     }
 
@@ -609,16 +396,16 @@
         TurnOffBacklight();
     }
 
-    Service* boot_anim = ServiceList::GetInstance().FindService("bootanim");
-    Service* surface_flinger = ServiceList::GetInstance().FindService("surfaceflinger");
-    if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {
+    Service* bootAnim = ServiceList::GetInstance().FindService("bootanim");
+    Service* surfaceFlinger = ServiceList::GetInstance().FindService("surfaceflinger");
+    if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
         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.
-            boot_anim->Stop();
+            bootAnim->Stop();
         }
 
         for (const auto& service : ServiceList::GetInstance()) {
@@ -628,39 +415,78 @@
 
             // start all animation classes if stopped.
             if (do_shutdown_animation) {
-                service->Start();
+                service->Start().IgnoreError();
             }
             service->SetShutdownCritical();  // will not check animation class separately
         }
 
         if (do_shutdown_animation) {
-            boot_anim->Start();
-            surface_flinger->SetShutdownCritical();
-            boot_anim->SetShutdownCritical();
+            bootAnim->Start().IgnoreError();
+            surfaceFlinger->SetShutdownCritical();
+            bootAnim->SetShutdownCritical();
         }
     }
 
     // optional shutdown step
     // 1. terminate all services except shutdown critical ones. wait for delay to finish
     if (shutdown_timeout > 0ms) {
-        StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
+        LOG(INFO) << "terminating init services";
+
+        // Ask all services to terminate except shutdown critical ones.
+        for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
+            if (!s->IsShutdownCritical()) s->Terminate();
+        }
+
+        int service_count = 0;
+        // Only wait up to half of timeout here
+        auto termination_wait_timeout = shutdown_timeout / 2;
+        while (t.duration() < termination_wait_timeout) {
+            ReapAnyOutstandingChildren();
+
+            service_count = 0;
+            for (const auto& s : ServiceList::GetInstance()) {
+                // Count the number of services running except shutdown critical.
+                // Exclude the console as it will ignore the SIGTERM signal
+                // and not exit.
+                // Note: SVC_CONSOLE actually means "requires console" but
+                // it is only used by the shell.
+                if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
+                    service_count++;
+                }
+            }
+
+            if (service_count == 0) {
+                // All terminable services terminated. We can exit early.
+                break;
+            }
+
+            // Wait a bit before recounting the number or running services.
+            std::this_thread::sleep_for(50ms);
+        }
+        LOG(INFO) << "Terminating running services took " << t
+                  << " with remaining services:" << service_count;
     }
-    // Send SIGKILL to ones that didn't terminate cleanly.
-    StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);
+
+    // minimum safety steps before restarting
+    // 2. kill all services except ones that are necessary for the shutdown sequence.
+    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
+        if (!s->IsShutdownCritical()) s->Stop();
+    }
     SubcontextTerminate();
-    // Reap subcontext pids.
     ReapAnyOutstandingChildren();
 
     // 3. send volume shutdown to vold
-    Service* vold_service = ServiceList::GetInstance().FindService("vold");
-    if (vold_service != nullptr && vold_service->IsRunning()) {
+    Service* voldService = ServiceList::GetInstance().FindService("vold");
+    if (voldService != nullptr && voldService->IsRunning()) {
         ShutdownVold();
-        vold_service->Stop();
+        voldService->Stop();
     } else {
         LOG(INFO) << "vold not running, skipping vold shutdown";
     }
     // logcat stopped here
-    StopServices(GetDebuggingServices(false /* only_post_data */), 0ms, false /* SIGKILL */);
+    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
+        if (kill_after_apps.count(s->name())) s->Stop();
+    }
     // 4. sync, try umount, and optionally run fsck for user shutdown
     {
         Timer sync_timer;
@@ -671,8 +497,7 @@
     // 5. drop caches and disable zram backing device, if exist
     KillZramBackingDevice();
 
-    UmountStat stat =
-            TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);
+    UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
     // Follow what linux shutdown is doing: one more sync with little bit delay
     {
         Timer sync_timer;
@@ -682,128 +507,23 @@
     }
     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, reboot_target);
+    RebootSystem(cmd, rebootTarget);
     abort();
 }
 
-static void EnterShutdown() {
-    LOG(INFO) << "Entering shutdown mode";
-    shutting_down = true;
-    // Skip wait for prop if it is in progress
-    ResetWaitForProp();
-    // Clear EXEC flag if there is one pending
-    for (const auto& s : ServiceList::GetInstance()) {
-        s->UnSetExec();
-    }
-    // We no longer process messages about properties changing coming from property service, so we
-    // need to tell property service to stop sending us these messages, otherwise it'll fill the
-    // buffers and block indefinitely, causing future property sets, including those that init makes
-    // during shutdown in Service::NotifyStateChange() to also block indefinitely.
-    SendStopSendingMessagesMessage();
-}
-
-static void LeaveShutdown() {
-    LOG(INFO) << "Leaving shutdown mode";
-    shutting_down = false;
-    SendStartSendingMessagesMessage();
-}
-
-static Result<void> UnmountAllApexes() {
-    const char* args[] = {"/system/bin/apexd", "--unmount-all"};
-    int status;
-    if (logwrap_fork_execvp(arraysize(args), args, &status, false, LOG_KLOG, true, nullptr) != 0) {
-        return ErrnoError() << "Failed to call '/system/bin/apexd --unmount-all'";
-    }
-    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
-        return {};
-    }
-    return Error() << "'/system/bin/apexd --unmount-all' failed : " << status;
-}
-
-static Result<void> DoUserspaceReboot() {
-    LOG(INFO) << "Userspace reboot initiated";
-    auto guard = android::base::make_scope_guard([] {
-        // Leave shutdown so that we can handle a full reboot.
-        LeaveShutdown();
-        TriggerShutdown("reboot,abort-userspace-reboot");
-    });
-    // Triggering userspace-reboot-requested will result in a bunch of set_prop
-    // actions. We should make sure, that all of them are propagated before
-    // proceeding with userspace reboot.
-    // TODO(b/135984674): implement proper synchronization logic.
-    std::this_thread::sleep_for(500ms);
-    EnterShutdown();
-    std::vector<Service*> stop_first;
-    // Remember the services that were enabled. We will need to manually enable them again otherwise
-    // triggers like class_start won't restart them.
-    std::vector<Service*> were_enabled;
-    stop_first.reserve(ServiceList::GetInstance().services().size());
-    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
-        if (s->is_post_data() && !kDebuggingServices.count(s->name())) {
-            stop_first.push_back(s);
-        }
-        if (s->is_post_data() && s->IsEnabled()) {
-            were_enabled.push_back(s);
-        }
-    }
-    // TODO(b/135984674): do we need shutdown animation for userspace reboot?
-    // TODO(b/135984674): control userspace timeout via read-only property?
-    StopServicesAndLogViolations(stop_first, 10s, true /* SIGTERM */);
-    if (int r = StopServicesAndLogViolations(stop_first, 20s, false /* SIGKILL */); r > 0) {
-        // TODO(b/135984674): store information about offending services for debugging.
-        return Error() << r << " post-data services are still running";
-    }
-    // TODO(b/143970043): in case of ext4 we probably we will need to restart vold and kill zram
-    //  backing device.
-    if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */), 5s,
-                                             false /* SIGKILL */);
-        r > 0) {
-        // TODO(b/135984674): store information about offending services for debugging.
-        return Error() << r << " debugging services are still running";
-    }
-    if (auto result = UnmountAllApexes(); !result) {
-        return result;
-    }
-    if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
-        return Error() << "Failed to switch to bootstrap namespace";
-    }
-    // Re-enable services
-    for (const auto& s : were_enabled) {
-        LOG(INFO) << "Re-enabling service '" << s->name() << "'";
-        s->Enable();
-    }
-    LeaveShutdown();
-    ActionManager::GetInstance().QueueEventTrigger("userspace-reboot-resume");
-    guard.Disable();  // Go on with userspace reboot.
-    return {};
-}
-
-static void HandleUserspaceReboot() {
-    LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
-    auto& am = ActionManager::GetInstance();
-    am.ClearQueue();
-    am.QueueEventTrigger("userspace-reboot-requested");
-    auto handler = [](const BuiltinArguments&) { return DoUserspaceReboot(); };
-    am.QueueBuiltinAction(handler, "userspace-reboot");
-}
-
-void HandlePowerctlMessage(const std::string& command) {
+bool HandlePowerctlMessage(const std::string& command) {
     unsigned int cmd = 0;
     std::vector<std::string> cmd_params = Split(command, ",");
     std::string reboot_target = "";
     bool run_fsck = false;
     bool command_invalid = false;
-    bool userspace_reboot = false;
 
-    if (cmd_params[0] == "shutdown") {
+    if (cmd_params.size() > 3) {
+        command_invalid = true;
+    } else if (cmd_params[0] == "shutdown") {
         cmd = ANDROID_RB_POWEROFF;
-        if (cmd_params.size() >= 2) {
+        if (cmd_params.size() == 2) {
             if (cmd_params[1] == "userrequested") {
                 // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
                 // Run fsck once the file system is remounted in read-only mode.
@@ -819,10 +539,6 @@
         cmd = ANDROID_RB_RESTART2;
         if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
-            if (reboot_target == "userspace") {
-                LOG(INFO) << "Userspace reboot requested";
-                userspace_reboot = true;
-            }
             // adb reboot fastboot should boot into bootloader for devices not
             // supporting logical partitions.
             if (reboot_target == "fastboot" &&
@@ -838,20 +554,6 @@
                                   "bootloader_message: "
                                << err;
                 }
-            } else if (reboot_target == "recovery") {
-                bootloader_message boot = {};
-                if (std::string err; !read_bootloader_message(&boot, &err)) {
-                    LOG(ERROR) << "Failed to read bootloader message: " << err;
-                }
-                // Update the boot command field if it's empty, and preserve
-                // the other arguments in the bootloader message.
-                if (boot.command[0] == '\0') {
-                    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
-                    if (std::string err; !write_bootloader_message(boot, &err)) {
-                        LOG(ERROR) << "Failed to set bootloader message: " << err;
-                        return;
-                    }
-                }
             } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
                        reboot_target == "fastboot") {
                 std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
@@ -862,14 +564,14 @@
                 std::string err;
                 if (!write_bootloader_message(options, &err)) {
                     LOG(ERROR) << "Failed to set bootloader message: " << err;
-                    return;
+                    return false;
                 }
                 reboot_target = "recovery";
             }
 
-            // If there are additional parameter, pass them along
-            for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {
-                reboot_target += "," + cmd_params[i];
+            // If there is an additional parameter, pass it along
+            if ((cmd_params.size() == 3) && cmd_params[2].size()) {
+                reboot_target += "," + cmd_params[2];
             }
         }
     } else {
@@ -877,12 +579,7 @@
     }
     if (command_invalid) {
         LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
-        return;
-    }
-
-    if (userspace_reboot) {
-        HandleUserspaceReboot();
-        return;
+        return false;
     }
 
     LOG(INFO) << "Clear action queue and start shutdown trigger";
@@ -892,15 +589,19 @@
     // Queue built-in shutdown_done
     auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
         DoReboot(cmd, command, reboot_target, run_fsck);
-        return Result<void>{};
+        return Success();
     };
     ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
 
-    EnterShutdown();
-}
+    // Skip wait for prop if it is in progress
+    ResetWaitForProp();
 
-bool IsShuttingDown() {
-    return shutting_down;
+    // Clear EXEC flag if there is one pending
+    for (const auto& s : ServiceList::GetInstance()) {
+        s->UnSetExec();
+    }
+
+    return true;
 }
 
 }  // namespace init
diff --git a/init/reboot.h b/init/reboot.h
index 81c3edc..07dcb6e 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -23,9 +23,8 @@
 namespace init {
 
 // Parses and handles a setprop sys.powerctl message.
-void HandlePowerctlMessage(const std::string& command);
+bool HandlePowerctlMessage(const std::string& command);
 
-bool IsShuttingDown();
 }  // namespace init
 }  // namespace android
 
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index dac0cf4..de085cc 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -109,7 +109,7 @@
     abort();
 }
 
-void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
+void __attribute__((noreturn)) InitFatalReboot() {
     auto pid = fork();
 
     if (pid == -1) {
@@ -124,7 +124,6 @@
     }
 
     // In the parent, let's try to get a backtrace then shutdown.
-    LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
     std::unique_ptr<Backtrace> backtrace(
             Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
     if (!backtrace->Unwind(0)) {
@@ -155,7 +154,7 @@
         // 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(signal);
+        InitFatalReboot();
     };
     action.sa_flags = SA_RESTART;
     sigaction(SIGABRT, &action, nullptr);
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 878ad96..3fd969e 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -27,7 +27,7 @@
 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(int signal_number);
+void __attribute__((noreturn)) InitFatalReboot();
 void InstallRebootSignalHandlers();
 
 }  // namespace init
diff --git a/init/result.h b/init/result.h
index b70dd1b..0e3fd3d 100644
--- a/init/result.h
+++ b/init/result.h
@@ -14,16 +14,200 @@
  * limitations under the License.
  */
 
-#pragma once
+// 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.
 
-// The implementation of this file has moved to android-base.  This file remains since historically,
-// these classes were a part of init.
+// 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>.
 
-#include <android-base/result.h>
+// 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);
 
-using android::base::ErrnoError;
-using android::base::ErrnoErrorf;
-using android::base::Error;
-using android::base::Errorf;
-using android::base::Result;
-using android::base::ResultError;
+#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 [[nodiscard]] 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) {}
+
+    void IgnoreError() const {}
+
+    bool has_value() const { return contents_.index() == 0; }
+
+    T& value() & { return std::get<0>(contents_); }
+    const T& value() const & { return std::get<0>(contents_); }
+    T&& value() && { return std::get<0>(std::move(contents_)); }
+    const T&& value() const && { return std::get<0>(std::move(contents_)); }
+
+    const ResultError& error() const & { return std::get<1>(contents_); }
+    ResultError&& error() && { return std::get<1>(std::move(contents_)); }
+    const ResultError&& error() const && { return std::get<1>(std::move(contents_)); }
+
+    const std::string& error_string() const & { return std::get<1>(contents_).error_string; }
+    std::string&& error_string() && { return std::get<1>(std::move(contents_)).error_string; }
+    const std::string&& error_string() const && {
+        return std::get<1>(std::move(contents_)).error_string;
+    }
+
+    int error_errno() const { return std::get<1>(contents_).error_errno; }
+
+    explicit operator bool() const { return has_value(); }
+
+    T& operator*() & { return value(); }
+    const T& operator*() const & { return value(); }
+    T&& operator*() && { return std::move(value()); }
+    const T&& operator*() const && { return std::move(value()); }
+
+    T* operator->() { return &value(); }
+    const T* operator->() const { return &value(); }
+
+  private:
+    std::variant<T, ResultError> contents_;
+};
+
+using Success = std::monostate;
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/result_test.cpp b/init/result_test.cpp
new file mode 100644
index 0000000..327b444
--- /dev/null
+++ b/init/result_test.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "result.h"
+
+#include "errno.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+TEST(result, result_accessors) {
+    Result<std::string> result = "success";
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ("success", *result);
+    EXPECT_EQ("success", result.value());
+
+    EXPECT_EQ('s', result->data()[0]);
+}
+
+TEST(result, result_accessors_rvalue) {
+    ASSERT_TRUE(Result<std::string>("success"));
+    ASSERT_TRUE(Result<std::string>("success").has_value());
+
+    EXPECT_EQ("success", *Result<std::string>("success"));
+    EXPECT_EQ("success", Result<std::string>("success").value());
+
+    EXPECT_EQ('s', Result<std::string>("success")->data()[0]);
+}
+
+TEST(result, result_success) {
+    Result<Success> result = Success();
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ(Success(), *result);
+    EXPECT_EQ(Success(), result.value());
+}
+
+TEST(result, result_success_rvalue) {
+    // Success() doesn't actually create a Result<Success> object, but rather an object that can be
+    // implicitly constructed into a Result<Success> object.
+
+    auto MakeRvalueSuccessResult = []() -> Result<Success> { return Success(); };
+    ASSERT_TRUE(MakeRvalueSuccessResult());
+    ASSERT_TRUE(MakeRvalueSuccessResult().has_value());
+
+    EXPECT_EQ(Success(), *MakeRvalueSuccessResult());
+    EXPECT_EQ(Success(), MakeRvalueSuccessResult().value());
+}
+
+TEST(result, result_error) {
+    Result<Success> result = Error() << "failure" << 1;
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ("failure1", result.error_string());
+}
+
+TEST(result, result_error_empty) {
+    Result<Success> result = Error();
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ("", result.error_string());
+}
+
+TEST(result, result_error_rvalue) {
+    // Error() and ErrnoError() aren't actually used to create a Result<T> object.
+    // Under the hood, they are an intermediate class that can be implicitly constructed into a
+    // Result<T>.  This is needed both to create the ostream and because Error() itself, by
+    // definition will not know what the type, T, of the underlying Result<T> object that it would
+    // create is.
+
+    auto MakeRvalueErrorResult = []() -> Result<Success> { return Error() << "failure" << 1; };
+    ASSERT_FALSE(MakeRvalueErrorResult());
+    ASSERT_FALSE(MakeRvalueErrorResult().has_value());
+
+    EXPECT_EQ(0, MakeRvalueErrorResult().error_errno());
+    EXPECT_EQ("failure1", MakeRvalueErrorResult().error_string());
+}
+
+TEST(result, result_errno_error) {
+    constexpr int test_errno = 6;
+    errno = test_errno;
+    Result<Success> result = ErrnoError() << "failure" << 1;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ("failure1: "s + strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_errno_error_no_text) {
+    constexpr int test_errno = 6;
+    errno = test_errno;
+    Result<Success> result = ErrnoError();
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ(strerror(test_errno), result.error_string());
+}
+
+TEST(result, result_error_from_other_result) {
+    auto error_text = "test error"s;
+    Result<Success> result = Error() << error_text;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_error_through_ostream) {
+    auto error_text = "test error"s;
+    Result<Success> result = Error() << error_text;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = Error() << result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(0, result.error_errno());
+    EXPECT_EQ(error_text, result.error_string());
+}
+
+TEST(result, result_errno_error_through_ostream) {
+    auto error_text = "test error"s;
+    constexpr int test_errno = 6;
+    errno = 6;
+    Result<Success> result = ErrnoError() << error_text;
+
+    errno = 0;
+
+    ASSERT_FALSE(result);
+    ASSERT_FALSE(result.has_value());
+
+    Result<std::string> result2 = Error() << result.error();
+
+    ASSERT_FALSE(result2);
+    ASSERT_FALSE(result2.has_value());
+
+    EXPECT_EQ(test_errno, result.error_errno());
+    EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error_string());
+}
+
+TEST(result, constructor_forwarding) {
+    auto result = Result<std::string>(5, 'a');
+
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(result.has_value());
+
+    EXPECT_EQ("aaaaa", *result);
+}
+
+struct ConstructorTracker {
+    static size_t constructor_called;
+    static size_t copy_constructor_called;
+    static size_t move_constructor_called;
+    static size_t copy_assignment_called;
+    static size_t move_assignment_called;
+
+    template <typename T>
+    ConstructorTracker(T&& string) : string(string) {
+        ++constructor_called;
+    }
+
+    ConstructorTracker(const ConstructorTracker& ct) {
+        ++copy_constructor_called;
+        string = ct.string;
+    }
+    ConstructorTracker(ConstructorTracker&& ct) noexcept {
+        ++move_constructor_called;
+        string = std::move(ct.string);
+    }
+    ConstructorTracker& operator=(const ConstructorTracker& ct) {
+        ++copy_assignment_called;
+        string = ct.string;
+        return *this;
+    }
+    ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
+        ++move_assignment_called;
+        string = std::move(ct.string);
+        return *this;
+    }
+
+    std::string string;
+};
+
+size_t ConstructorTracker::constructor_called = 0;
+size_t ConstructorTracker::copy_constructor_called = 0;
+size_t ConstructorTracker::move_constructor_called = 0;
+size_t ConstructorTracker::copy_assignment_called = 0;
+size_t ConstructorTracker::move_assignment_called = 0;
+
+Result<ConstructorTracker> ReturnConstructorTracker(const std::string& in) {
+    if (in.empty()) {
+        return "literal string";
+    }
+    if (in == "test2") {
+        return ConstructorTracker(in + in + "2");
+    }
+    ConstructorTracker result(in + " " + in);
+    return result;
+};
+
+TEST(result, no_copy_on_return) {
+    // If returning parameters that may be used to implicitly construct the type T of Result<T>,
+    // then those parameters are forwarded to the construction of Result<T>.
+
+    // If returning an prvalue or xvalue, it will be move constructed during the construction of
+    // Result<T>.
+
+    // This check ensures that that is the case, and particularly that no copy constructors
+    // are called.
+
+    auto result1 = ReturnConstructorTracker("");
+    ASSERT_TRUE(result1);
+    EXPECT_EQ("literal string", result1->string);
+    EXPECT_EQ(1U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+    auto result2 = ReturnConstructorTracker("test2");
+    ASSERT_TRUE(result2);
+    EXPECT_EQ("test2test22", result2->string);
+    EXPECT_EQ(2U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+
+    auto result3 = ReturnConstructorTracker("test3");
+    ASSERT_TRUE(result3);
+    EXPECT_EQ("test3 test3", result3->string);
+    EXPECT_EQ(3U, ConstructorTracker::constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
+    EXPECT_EQ(2U, ConstructorTracker::move_constructor_called);
+    EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
+    EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
+}
+
+// Below two tests require that we do not hide the move constructor with our forwarding reference
+// constructor.  This is done with by disabling the forwarding reference constructor if its first
+// and only type is Result<T>.
+TEST(result, result_result_with_success) {
+    auto return_result_result_with_success = []() -> Result<Result<Success>> {
+        return Result<Success>();
+    };
+    auto result = return_result_result_with_success();
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(*result);
+
+    auto inner_result = result.value();
+    ASSERT_TRUE(inner_result);
+}
+
+TEST(result, result_result_with_failure) {
+    auto return_result_result_with_error = []() -> Result<Result<Success>> {
+        return Result<Success>(ResultError("failure string", 6));
+    };
+    auto result = return_result_result_with_error();
+    ASSERT_TRUE(result);
+    ASSERT_FALSE(*result);
+    EXPECT_EQ("failure string", result->error_string());
+    EXPECT_EQ(6, result->error_errno());
+}
+
+// This test requires that we disable the forwarding reference constructor if Result<T> is the
+// *only* type that we are forwarding.  In otherwords, if we are forwarding Result<T>, int to
+// construct a Result<T>, then we still need the constructor.
+TEST(result, result_two_parameter_constructor_same_type) {
+    struct TestStruct {
+        TestStruct(int value) : value_(value) {}
+        TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
+        int value_;
+    };
+
+    auto return_test_struct = []() -> Result<TestStruct> { return {Result<TestStruct>(6), 6}; };
+
+    auto result = return_test_struct();
+    ASSERT_TRUE(result);
+    EXPECT_EQ(36, result->value_);
+}
+
+TEST(result, die_on_access_failed_result) {
+    Result<std::string> result = Error();
+    ASSERT_DEATH(*result, "");
+}
+
+TEST(result, die_on_get_error_succesful_result) {
+    Result<std::string> result = "success";
+    ASSERT_DEATH(result.error_string(), "");
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
index 476a46a..1e0754a 100644
--- a/init/rlimit_parser.cpp
+++ b/init/rlimit_parser.cpp
@@ -77,7 +77,7 @@
         return Error() << "Could not parse hard limit '" << args[3] << "'";
     }
 
-    return std::pair{resource, limit};
+    return {resource, limit};
 }
 
 }  // namespace init
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
index 6a16d3b..659ba8a 100644
--- a/init/rlimit_parser_test.cpp
+++ b/init/rlimit_parser_test.cpp
@@ -43,8 +43,8 @@
     auto result = ParseRlimit(input);
 
     ASSERT_FALSE(result) << "input: " << input[1];
-    EXPECT_EQ(expected_result, result.error().message());
-    EXPECT_EQ(0, result.error().code());
+    EXPECT_EQ(expected_result, result.error_string());
+    EXPECT_EQ(0, result.error_errno());
 }
 
 TEST(rlimit, RlimitSuccess) {
diff --git a/init/security.cpp b/init/security.cpp
index 586d0c7..a3494a2 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<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
     unique_fd hwrandom_fd(
         TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (hwrandom_fd == -1) {
         if (errno == ENOENT) {
             LOG(INFO) << "/dev/hw_random not found";
             // It's not an error to not have a Hardware RNG.
-            return {};
+            return Success();
         }
         return ErrnoError() << "Failed to open /dev/hw_random";
     }
@@ -80,10 +80,10 @@
     }
 
     LOG(INFO) << "Mixed " << total_bytes_written << " bytes from /dev/hw_random into /dev/urandom";
-    return {};
+    return Success();
 }
 
-static bool SetHighestAvailableOptionValue(const std::string& path, int min, int max) {
+static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
     std::ifstream inf(path, std::fstream::in);
     if (!inf) {
         LOG(ERROR) << "Cannot open for reading: " << path;
@@ -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<void> SetMmapRndBitsAction(const BuiltinArguments&) {
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&) {
 // values are arch-dependent
 #if defined(USER_MODE_LINUX)
     // uml does not support mmap_rnd_bits
-    return {};
+    return Success();
 #elif defined(__aarch64__)
     // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
     if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
-        return {};
+        return Success();
     }
 #elif defined(__x86_64__)
     // x86_64 supports 28 - 32 bits
     if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
-        return {};
+        return Success();
     }
 #elif defined(__arm__) || defined(__i386__)
     // check to see if we're running on 64-bit kernel
     bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);
     // supported 32-bit architecture must have 16 bits set
     if (SetMmapRndBitsMin(16, 16, h64)) {
-        return {};
+        return Success();
     }
 #elif defined(__mips__) || defined(__mips64__)
     // TODO: add mips support b/27788820
-    return {};
+    return Success();
 #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<void> SetKptrRestrictAction(const BuiltinArguments&) {
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
     std::string path = KPTR_RESTRICT_PATH;
 
     if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
         LOG(FATAL) << "Unable to set adequate kptr_restrict value!";
         return Error();
     }
-    return {};
+    return Success();
 }
 
 }  // namespace init
diff --git a/init/security.h b/init/security.h
index b081a05..6f6b944 100644
--- a/init/security.h
+++ b/init/security.h
@@ -26,9 +26,9 @@
 namespace android {
 namespace init {
 
-Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
-Result<void> SetMmapRndBitsAction(const BuiltinArguments&);
-Result<void> SetKptrRestrictAction(const BuiltinArguments&);
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/selabel.cpp b/init/selabel.cpp
deleted file mode 100644
index daeb832..0000000
--- a/init/selabel.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 5d590b2..0000000
--- a/init/selabel.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 a9cd290..86238b4 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -36,25 +36,21 @@
 // The split SEPolicy is loaded as described below:
 // 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, /system_ext 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, system_ext and
-//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this
-//    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
-// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
-//    have been updated out of sync with /vendor (or /odm if it is present) 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.
+//    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 <linux/audit.h>
-#include <linux/netlink.h>
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -83,6 +79,8 @@
 
 namespace {
 
+selabel_handle* sehandle = nullptr;
+
 enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
 
 EnforcingStatus StatusFromCmdline() {
@@ -230,13 +228,6 @@
                       "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
         return false;
     }
-    std::string actual_system_ext_id;
-    if (!ReadFirstLine("/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256",
-                       &actual_system_ext_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256";
-        return false;
-    }
     std::string actual_product_id;
     if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
                        &actual_product_id)) {
@@ -252,13 +243,6 @@
         file->clear();
         return false;
     }
-    std::string precompiled_system_ext_id;
-    std::string precompiled_system_ext_sha256 = *file + ".system_ext_sepolicy_and_mapping.sha256";
-    if (!ReadFirstLine(precompiled_system_ext_sha256.c_str(), &precompiled_system_ext_id)) {
-        PLOG(INFO) << "Failed to read " << precompiled_system_ext_sha256;
-        file->clear();
-        return false;
-    }
     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)) {
@@ -267,7 +251,6 @@
         return false;
     }
     if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
-        actual_system_ext_id.empty() || actual_system_ext_id != precompiled_system_ext_id ||
         actual_product_id.empty() || actual_product_id != precompiled_product_id) {
         file->clear();
         return false;
@@ -347,23 +330,6 @@
     }
     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 system_ext_policy_cil_file("/system_ext/etc/selinux/system_ext_sepolicy.cil");
-    if (access(system_ext_policy_cil_file.c_str(), F_OK) == -1) {
-        system_ext_policy_cil_file.clear();
-    }
-
-    std::string system_ext_mapping_file("/system_ext/etc/selinux/mapping/" + vend_plat_vers +
-                                        ".cil");
-    if (access(system_ext_mapping_file.c_str(), F_OK) == -1) {
-        system_ext_mapping_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();
@@ -409,15 +375,6 @@
     };
     // clang-format on
 
-    if (!plat_compat_cil_file.empty()) {
-        compile_args.push_back(plat_compat_cil_file.c_str());
-    }
-    if (!system_ext_policy_cil_file.empty()) {
-        compile_args.push_back(system_ext_policy_cil_file.c_str());
-    }
-    if (!system_ext_mapping_file.empty()) {
-        compile_args.push_back(system_ext_mapping_file.c_str());
-    }
     if (!product_policy_cil_file.empty()) {
         compile_args.push_back(product_policy_cil_file.c_str());
     }
@@ -464,6 +421,8 @@
 }
 
 void SelinuxInitialize() {
+    Timer t;
+
     LOG(INFO) << "Loading SELinux policy";
     if (!LoadPolicy()) {
         LOG(FATAL) << "Unable to load SELinux policy";
@@ -473,47 +432,23 @@
     bool is_enforcing = IsEnforcing();
     if (kernel_enforcing != is_enforcing) {
         if (security_setenforce(is_enforcing)) {
-            PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")
-                        << ") failed";
+            PLOG(FATAL) << "security_setenforce(%s) failed" << (is_enforcing ? "true" : "false");
         }
     }
 
     if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
         LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
     }
-}
 
-constexpr size_t kKlogMessageSize = 1024;
-
-void SelinuxAvcLog(char* buf, size_t buf_len) {
-    CHECK_GT(buf_len, 0u);
-
-    size_t str_len = strnlen(buf, buf_len);
-    // trim newline at end of string
-    if (buf[str_len - 1] == '\n') {
-        buf[str_len - 1] = '\0';
-    }
-
-    struct NetlinkMessage {
-        nlmsghdr hdr;
-        char buf[kKlogMessageSize];
-    } request = {};
-
-    request.hdr.nlmsg_flags = NLM_F_REQUEST;
-    request.hdr.nlmsg_type = AUDIT_USER_AVC;
-    request.hdr.nlmsg_len = sizeof(request);
-    strlcpy(request.buf, buf, sizeof(request.buf));
-
-    auto fd = unique_fd{socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT)};
-    if (!fd.ok()) {
-        return;
-    }
-
-    TEMP_FAILURE_RETRY(send(fd, &request, sizeof(request), 0));
+    // 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.
 void SelinuxRestoreContext() {
     LOG(INFO) << "Running restorecon...";
     selinux_android_restorecon("/dev", 0);
@@ -541,63 +476,53 @@
     } else if (type == SELINUX_INFO) {
         severity = android::base::INFO;
     }
-    char buf[kKlogMessageSize];
+    char buf[1024];
     va_list ap;
     va_start(ap, fmt);
-    int length_written = vsnprintf(buf, sizeof(buf), fmt, ap);
+    vsnprintf(buf, sizeof(buf), fmt, ap);
     va_end(ap);
-    if (length_written <= 0) {
-        return 0;
-    }
-    if (type == SELINUX_AVC) {
-        SelinuxAvcLog(buf, sizeof(buf));
-    } else {
-        android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
-    }
+    android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
     return 0;
 }
 
+// This function sets up SELinux logging to be written to kmsg, to match init's logging.
 void SelinuxSetupKernelLogging() {
     selinux_callback cb;
     cb.func_log = SelinuxKlogCallback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 }
 
+// This function 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() {
-    static int vendor_android_version = [] {
-        if (!IsSplitPolicyDevice()) {
-            // 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__;
-        }
+    if (!IsSplitPolicyDevice()) {
+        // 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)) {
-            LOG(FATAL) << "Could not read vendor SELinux version";
-        }
+    std::string version;
+    if (!GetVendorMappingVersion(&version)) {
+        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(FATAL) << "Failed to parse the vendor sepolicy major version "
-                        << major_version_str;
-        }
+    int major_version;
+    std::string major_version_str(version, 0, version.find('.'));
+    if (!ParseInt(major_version_str, &major_version)) {
+        PLOG(FATAL) << "Failed to parse the vendor sepolicy major version " << major_version_str;
+    }
 
-        return major_version;
-    }();
-    return vendor_android_version;
+    return major_version;
 }
 
+// This function initializes SELinux then execs init to run in the init SELinux context.
 int SetupSelinux(char** argv) {
-    SetStdioToDevNull(argv);
     InitKernelLogging(argv);
 
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
     }
 
-    boot_clock::time_point start_time = boot_clock::now();
-
     // Set up SELinux, loading the SELinux policy.
     SelinuxSetupKernelLogging();
     SelinuxInitialize();
@@ -610,8 +535,6 @@
         PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
     }
 
-    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));
@@ -623,5 +546,54 @@
     return 1;
 }
 
+// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
+// its value.  selinux_android_restorecon() also needs an sehandle for file context look up.  It
+// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
+// one, thus eliminating an extra call to selinux_android_file_context_handle().
+void SelabelInitialize() {
+    sehandle = selinux_android_file_context_handle();
+    selinux_android_set_sehandle(sehandle);
+}
+
+// A C++ wrapper around selabel_lookup() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+    result->clear();
+
+    if (!sehandle) return true;
+
+    char* context;
+    if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {
+        return false;
+    }
+    *result = context;
+    free(context);
+    return true;
+}
+
+// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.
+// If sehandle is null, this returns success with an empty context.
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+                                       const std::vector<std::string>& aliases, int type,
+                                       std::string* result) {
+    result->clear();
+
+    if (!sehandle) return true;
+
+    std::vector<const char*> c_aliases;
+    for (const auto& alias : aliases) {
+        c_aliases.emplace_back(alias.c_str());
+    }
+    c_aliases.emplace_back(nullptr);
+
+    char* context;
+    if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {
+        return false;
+    }
+    *result = context;
+    free(context);
+    return true;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/selinux.h b/init/selinux.h
index 1a41bfd..3aa9406 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -14,27 +14,28 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_SELINUX_H
+#define _INIT_SELINUX_H
+
+#include <string>
+#include <vector>
 
 namespace android {
 namespace init {
 
-// Initialize SELinux, then exec init to run in the init SELinux context.
 int SetupSelinux(char** argv);
-
-// Restore the proper security context to files and directories on ramdisk, and
-// those that were created before initial sepolicy load.
-// This must happen before /dev is populated by ueventd.
 void SelinuxRestoreContext();
 
-// Set up SELinux logging to be written to kmsg, to match init's logging.
 void SelinuxSetupKernelLogging();
-
-// Return the Android API level with which the vendor SEPolicy was compiled.
-// Used for version checks such as whether or not vendor_init should be used.
 int SelinuxGetVendorAndroidVersion();
 
-static constexpr char kEnvSelinuxStartedAt[] = "SELINUX_STARTED_AT";
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+bool SelabelLookupFileContextBestMatch(const std::string& key,
+                                       const std::vector<std::string>& aliases, int type,
+                                       std::string* result);
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/service.cpp b/init/service.cpp
index f8e98a2..2db548e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -18,34 +18,41 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <linux/input.h>
 #include <linux/securebits.h>
 #include <sched.h>
+#include <sys/mount.h>
 #include <sys/prctl.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/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/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/sockets.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 "lmkd_service.h"
-#include "service_list.h"
+#include "rlimit_parser.h"
 #include "util.h"
 
 #if defined(__ANDROID__)
-#include <ApexProperties.sysprop.h>
+#include <android/api-level.h>
+#include <sys/system_properties.h>
 
 #include "init.h"
 #include "mount_namespace.h"
 #include "property_service.h"
+#include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
@@ -53,9 +60,11 @@
 using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::Join;
-using android::base::make_scope_guard;
+using android::base::ParseInt;
+using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
+using android::base::unique_fd;
 using android::base::WriteStringToFile;
 
 namespace android {
@@ -97,6 +106,87 @@
     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", "sysfs", kSafeFlags, "") == -1) {
+            return ErrnoError() << "Could not mount(/sys)";
+        }
+    }
+    return Success();
+}
+
+Result<Success> Service::SetUpPidNamespace() const {
+    if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
+        return ErrnoError() << "Could not set name";
+    }
+
+    pid_t child_pid = fork();
+    if (child_pid == -1) {
+        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 == -1) {
+            return ErrnoError() << "Could not open namespace at " << path;
+        }
+        if (setns(fd, nstype) == -1) {
+            return ErrnoError() << "Could not setns() namespace at " << path;
+        }
+    }
+    return Success();
+}
+
 static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {
     std::vector<std::string> expanded_args;
     std::vector<char*> c_strings;
@@ -104,11 +194,9 @@
     expanded_args.resize(args.size());
     c_strings.push_back(const_cast<char*>(args[0].data()));
     for (std::size_t i = 1; i < args.size(); ++i) {
-        auto expanded_arg = ExpandProps(args[i]);
-        if (!expanded_arg) {
-            LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
+        if (!expand_props(args[i], &expanded_args[i])) {
+            LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
         }
-        expanded_args[i] = *expanded_arg;
         c_strings.push_back(expanded_args[i].data());
     }
     c_strings.push_back(nullptr);
@@ -120,10 +208,9 @@
     return execv(c_strings[0], c_strings.data()) == 0;
 }
 
-static bool AreRuntimeApexesReady() {
+static bool IsRuntimeApexReady() {
     struct stat buf;
-    return stat("/apex/com.android.art/", &buf) == 0 &&
-           stat("/apex/com.android.runtime/", &buf) == 0;
+    return stat("/apex/com.android.runtime/", &buf) == 0;
 }
 
 unsigned long Service::next_start_order_ = 1;
@@ -134,7 +221,7 @@
     : 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, int namespace_flags,
+                 const std::vector<gid_t>& supp_gids, unsigned namespace_flags,
                  const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
                  const std::vector<std::string>& args)
     : name_(name),
@@ -142,17 +229,17 @@
       flags_(flags),
       pid_(0),
       crash_count_(0),
-      proc_attr_{.ioprio_class = IoSchedClass_NONE,
-                 .ioprio_pri = 0,
-                 .uid = uid,
-                 .gid = gid,
-                 .supp_gids = supp_gids,
-                 .priority = 0},
-      namespaces_{.flags = namespace_flags},
+      uid_(uid),
+      gid_(gid),
+      supp_gids_(supp_gids),
+      namespace_flags_(namespace_flags),
       seclabel_(seclabel),
       onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
                  "onrestart", {}),
-      oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
+      ioprio_class_(IoSchedClass_NONE),
+      ioprio_pri_(0),
+      priority_(0),
+      oom_score_adjust_(-1000),
       start_order_(0),
       args_(args) {}
 
@@ -172,15 +259,6 @@
             property_set(boottime_property, std::to_string(start_ns));
         }
     }
-
-    // init.svc_debug_pid.* properties are only for tests, and should not be used
-    // on device for security checks.
-    std::string pid_property = "init.svc_debug_pid." + name_;
-    if (new_state == "running") {
-        property_set(pid_property, std::to_string(pid_));
-    } else if (new_state == "stopped") {
-        property_set(pid_property, "");
-    }
 }
 
 void Service::KillProcessGroup(int signal) {
@@ -193,22 +271,24 @@
                   << ") process group...";
         int r;
         if (signal == SIGTERM) {
-            r = killProcessGroupOnce(proc_attr_.uid, pid_, signal);
+            r = killProcessGroupOnce(uid_, pid_, signal);
         } else {
-            r = killProcessGroup(proc_attr_.uid, pid_, signal);
+            r = killProcessGroup(uid_, pid_, signal);
         }
 
         if (r == 0) process_cgroup_empty_ = true;
     }
-
-    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
-        LmkdUnregister(name_, pid_);
-    }
 }
 
-void Service::SetProcessAttributesAndCaps() {
+void Service::SetProcessAttributes() {
+    for (const auto& rlimit : rlimits_) {
+        if (setrlimit(rlimit.first, &rlimit.second) == -1) {
+            LOG(FATAL) << StringPrintf("setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed",
+                                       rlimit.first, rlimit.second.rlim_cur, rlimit.second.rlim_max);
+        }
+    }
     // Keep capabilites on uid change.
-    if (capabilities_ && proc_attr_.uid) {
+    if (capabilities_ && 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);
@@ -221,21 +301,37 @@
         }
     }
 
-    if (auto result = SetProcessAttributes(proc_attr_); !result) {
-        LOG(FATAL) << "cannot set attribute for " << name_ << ": " << result.error();
-    }
+    // TODO: work out why this fails for `console` then upgrade to FATAL.
+    if (setpgid(0, getpid()) == -1) PLOG(ERROR) << "setpgid failed for " << name_;
 
+    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_) {
         if (!SetCapsForExec(*capabilities_)) {
             LOG(FATAL) << "cannot set capabilities for " << name_;
         }
-    } else if (proc_attr_.uid) {
+    } else if (uid_) {
         // Inheritable caps can be non-zero when running in a container.
         if (!DropInheritableCaps()) {
             LOG(FATAL) << "cannot drop inheritable caps for " << name_;
@@ -248,21 +344,14 @@
         KillProcessGroup(SIGKILL);
     }
 
-    // Remove any socket resources we may have created.
-    for (const auto& socket : sockets_) {
-        auto path = ANDROID_SOCKET_DIR "/" + socket.name;
-        unlink(path.c_str());
-    }
+    // Remove any descriptor resources we may have created.
+    std::for_each(descriptors_.begin(), descriptors_.end(),
+                  std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
 
     for (const auto& f : reap_callbacks_) {
         f(siginfo);
     }
 
-    if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
-        LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
-        TriggerShutdown(*on_failure_reboot_target_);
-    }
-
     if (flags_ & SVC_EXEC) UnSetExec();
 
     if (flags_ & SVC_TEMPORARY) return;
@@ -283,17 +372,10 @@
         return;
     }
 
-#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) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
+    if (((flags_ & SVC_CRITICAL) || !pre_apexd_) && !(flags_ & SVC_RESTART)) {
         bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
         if (now < time_crashed_ + 4min || !boot_completed) {
             if (++crash_count_ > 4) {
@@ -305,7 +387,8 @@
                     LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
                                << (boot_completed ? "in 4 minutes" : "before boot completed");
                     // Notifies update_verifier and apexd
-                    property_set("sys.init.updatable_crashing", "1");
+                    property_set("ro.init.updatable_crashing_process_name", name_);
+                    property_set("ro.init.updatable_crashing", "1");
                 }
             }
         } else {
@@ -328,22 +411,457 @@
     LOG(INFO) << "service " << name_;
     LOG(INFO) << "  class '" << Join(classnames_, " ") << "'";
     LOG(INFO) << "  exec " << Join(args_, " ");
-    for (const auto& socket : sockets_) {
-        LOG(INFO) << "  socket " << socket.name;
-    }
-    for (const auto& file : files_) {
-        LOG(INFO) << "  file " << file.name;
-    }
+    std::for_each(descriptors_.begin(), descriptors_.end(),
+                  [] (const auto& info) { LOG(INFO) << *info; });
 }
 
+Result<Success> Service::ParseCapabilities(std::vector<std::string>&& args) {
+    capabilities_ = 0;
 
-Result<void> Service::ExecStart() {
-    auto reboot_on_failure = make_scope_guard([this] {
-        if (on_failure_reboot_target_) {
-            TriggerShutdown(*on_failure_reboot_target_);
+    if (!CapAmbientSupported()) {
+        return Error()
+               << "capabilities requested but the kernel does not support ambient capabilities";
+    }
+
+    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(std::vector<std::string>&& args) {
+    classnames_ = std::set<std::string>(args.begin() + 1, args.end());
+    return Success();
+}
+
+Result<Success> Service::ParseConsole(std::vector<std::string>&& args) {
+    flags_ |= SVC_CONSOLE;
+    console_ = args.size() > 1 ? "/dev/" + args[1] : "";
+    return Success();
+}
+
+Result<Success> Service::ParseCritical(std::vector<std::string>&& args) {
+    flags_ |= SVC_CRITICAL;
+    return Success();
+}
+
+Result<Success> Service::ParseDisabled(std::vector<std::string>&& args) {
+    flags_ |= SVC_DISABLED;
+    flags_ |= SVC_RC_DISABLED;
+    return Success();
+}
+
+Result<Success> Service::ParseEnterNamespace(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, std::move(args[2]));
+    return Success();
+}
+
+Result<Success> Service::ParseGroup(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(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(std::vector<std::string>&& args) {
+    const std::string& interface_name = args[1];
+    const std::string& instance_name = args[2];
+
+    FQName fq_name;
+    if (!FQName::parse(interface_name, &fq_name)) {
+        return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
+    }
+
+    if (!fq_name.isFullyQualified()) {
+        return Error() << "Interface name not fully-qualified '" << interface_name << "'";
+    }
+
+    if (fq_name.isValidValueName()) {
+        return Error() << "Interface name must not be a value name '" << interface_name << "'";
+    }
+
+    const std::string fullname = interface_name + "/" + instance_name;
+
+    for (const auto& svc : ServiceList::GetInstance()) {
+        if (svc->interfaces().count(fullname) > 0) {
+            return Error() << "Interface '" << fullname << "' redefined in " << name()
+                           << " but is already defined by " << svc->name();
+        }
+    }
+
+    interfaces_.insert(fullname);
+
+    return Success();
+}
+
+Result<Success> Service::ParseIoprio(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(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 Success();
+        }
+
+        args = Split(expanded, ",");
+        it = args.begin();
+    }
+
+    for (; it != args.end(); ++it) {
+        int code;
+        if (ParseInt(*it, &code, 0, KEY_MAX)) {
+            for (auto& key : keycodes_) {
+                if (key == code) return Error() << "duplicate keycode: " << *it;
+            }
+            keycodes_.insert(std::upper_bound(keycodes_.begin(), keycodes_.end(), code), code);
+        } else {
+            return Error() << "invalid keycode: " << *it;
+        }
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseOneshot(std::vector<std::string>&& args) {
+    flags_ |= SVC_ONESHOT;
+    return Success();
+}
+
+Result<Success> Service::ParseOnrestart(std::vector<std::string>&& args) {
+    args.erase(args.begin());
+    int line = onrestart_.NumCommands() + 1;
+    if (auto result = onrestart_.AddCommand(std::move(args), line); !result) {
+        return Error() << "cannot add Onrestart command: " << result.error();
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseNamespace(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(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(std::vector<std::string>&& args) {
+    override_ = true;
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgSwappiness(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(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::ParseMemcgLimitPercent(std::vector<std::string>&& args) {
+    if (!ParseInt(args[1], &limit_percent_, 0)) {
+        return Error() << "limit_percent value must be equal or greater than 0";
+    }
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgLimitProperty(std::vector<std::string>&& args) {
+    limit_property_ = std::move(args[1]);
+    return Success();
+}
+
+Result<Success> Service::ParseMemcgSoftLimitInBytes(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(std::vector<std::string>&& args) {
+    auto rlimit = ParseRlimit(args);
+    if (!rlimit) return rlimit.error();
+
+    rlimits_.emplace_back(*rlimit);
+    return Success();
+}
+
+Result<Success> Service::ParseRestartPeriod(std::vector<std::string>&& args) {
+    int period;
+    if (!ParseInt(args[1], &period, 5)) {
+        return Error() << "restart_period value must be an integer >= 5";
+    }
+    restart_period_ = std::chrono::seconds(period);
+    return Success();
+}
+
+Result<Success> Service::ParseSeclabel(std::vector<std::string>&& args) {
+    seclabel_ = std::move(args[1]);
+    return Success();
+}
+
+Result<Success> Service::ParseSigstop(std::vector<std::string>&& args) {
+    sigstop_ = true;
+    return Success();
+}
+
+Result<Success> Service::ParseSetenv(std::vector<std::string>&& args) {
+    environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));
+    return Success();
+}
+
+Result<Success> Service::ParseShutdown(std::vector<std::string>&& args) {
+    if (args[1] == "critical") {
+        flags_ |= SVC_SHUTDOWN_CRITICAL;
+        return Success();
+    }
+    return Error() << "Invalid shutdown option";
+}
+
+Result<Success> Service::ParseTimeoutPeriod(std::vector<std::string>&& args) {
+    int period;
+    if (!ParseInt(args[1], &period, 1)) {
+        return Error() << "timeout_period value must be an integer >= 1";
+    }
+    timeout_period_ = std::chrono::seconds(period);
+    return Success();
+}
+
+template <typename T>
+Result<Success> Service::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(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(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<Success> Service::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<Success> Service::ParseUser(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(std::vector<std::string>&& args) {
+    args.erase(args.begin());
+    writepid_files_ = std::move(args);
+    return Success();
+}
+
+Result<Success> Service::ParseUpdatable(std::vector<std::string>&& args) {
+    updatable_ = true;
+    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",
+                        {0,     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}},
+        {"file",        {2,     2,    &Service::ParseFile}},
+        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
+        {"interface",   {2,     2,    &Service::ParseInterface}},
+        {"ioprio",      {2,     2,    &Service::ParseIoprio}},
+        {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
+        {"memcg.limit_in_bytes",
+                        {1,     1,    &Service::ParseMemcgLimitInBytes}},
+        {"memcg.limit_percent",
+                        {1,     1,    &Service::ParseMemcgLimitPercent}},
+        {"memcg.limit_property",
+                        {1,     1,    &Service::ParseMemcgLimitProperty}},
+        {"memcg.soft_limit_in_bytes",
+                        {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
+        {"memcg.swappiness",
+                        {1,     1,    &Service::ParseMemcgSwappiness}},
+        {"namespace",   {1,     2,    &Service::ParseNamespace}},
+        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
+        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
+        {"oom_score_adjust",
+                        {1,     1,    &Service::ParseOomScoreAdjust}},
+        {"override",    {0,     0,    &Service::ParseOverride}},
+        {"priority",    {1,     1,    &Service::ParsePriority}},
+        {"restart_period",
+                        {1,     1,    &Service::ParseRestartPeriod}},
+        {"rlimit",      {3,     3,    &Service::ParseProcessRlimit}},
+        {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
+        {"setenv",      {2,     2,    &Service::ParseSetenv}},
+        {"shutdown",    {1,     1,    &Service::ParseShutdown}},
+        {"sigstop",     {0,     0,    &Service::ParseSigstop}},
+        {"socket",      {3,     6,    &Service::ParseSocket}},
+        {"timeout_period",
+                        {1,     1,    &Service::ParseTimeoutPeriod}},
+        {"updatable",   {0,     0,    &Service::ParseUpdatable}},
+        {"user",        {1,     1,    &Service::ParseUser}},
+        {"writepid",    {1,     kMax, &Service::ParseWritepid}},
+    };
+    // clang-format on
+    return option_parsers;
+}
+
+Result<Success> Service::ParseLine(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, std::move(args));
+}
+
+Result<Success> 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.
@@ -360,21 +878,14 @@
     flags_ |= SVC_EXEC;
     is_exec_service_running_ = true;
 
-    LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
-              << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
+    LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid_ << " gid "
+              << gid_ << "+" << supp_gids_.size() << " context "
               << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
 
-    reboot_on_failure.Disable();
-    return {};
+    return Success();
 }
 
-Result<void> Service::Start() {
-    auto reboot_on_failure = make_scope_guard([this] {
-        if (on_failure_reboot_target_) {
-            TriggerShutdown(*on_failure_reboot_target_);
-        }
-    });
-
+Result<Success> Service::Start() {
     if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
         ServiceList::GetInstance().DelayService(*this);
         return Error() << "Cannot start an updatable service '" << name_
@@ -397,22 +908,21 @@
             flags_ |= SVC_RESTART;
         }
         // It is not an error to try to start a service that is already running.
-        reboot_on_failure.Disable();
-        return {};
+        return Success();
     }
 
     bool needs_console = (flags_ & SVC_CONSOLE);
     if (needs_console) {
-        if (proc_attr_.console.empty()) {
-            proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
+        if (console_.empty()) {
+            console_ = default_console;
         }
 
         // Make sure that open call succeeds to ensure a console driver is
         // properly registered for the device node
-        int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
+        int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
         if (console_fd < 0) {
             flags_ |= SVC_DISABLED;
-            return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
+            return ErrnoError() << "Couldn't open console '" << console_ << "'";
         }
         close(console_fd);
     }
@@ -434,11 +944,11 @@
         scon = *result;
     }
 
-    if (!AreRuntimeApexesReady() && !pre_apexd_) {
-        // If this service is started before the Runtime and ART APEXes get
-        // 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.
+    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;
     }
 
@@ -446,26 +956,9 @@
 
     LOG(INFO) << "starting service '" << name_ << "'...";
 
-    std::vector<Descriptor> descriptors;
-    for (const auto& socket : sockets_) {
-        if (auto result = socket.Create(scon)) {
-            descriptors.emplace_back(std::move(*result));
-        } else {
-            LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
-        }
-    }
-
-    for (const auto& file : files_) {
-        if (auto result = file.Create()) {
-            descriptors.emplace_back(std::move(*result));
-        } else {
-            LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
-        }
-    }
-
     pid_t pid = -1;
-    if (namespaces_.flags) {
-        pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
+    if (namespace_flags_) {
+        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
     } else {
         pid = fork();
     }
@@ -473,30 +966,97 @@
     if (pid == 0) {
         umask(077);
 
-        if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result) {
-            LOG(FATAL) << "Service '" << name_
-                       << "' failed to set up namespaces: " << result.error();
+        if (auto result = EnterNamespaces(); !result) {
+            LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
+        }
+
+#if defined(__ANDROID__)
+        if (pre_apexd_) {
+            if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+                LOG(FATAL) << "Service '" << name_ << "' could not enter "
+                           << "into the bootstrap mount namespace";
+            }
+        }
+#endif
+
+        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();
+            }
         }
 
         for (const auto& [key, value] : environment_vars_) {
             setenv(key.c_str(), value.c_str(), 1);
         }
 
-        for (const auto& descriptor : descriptors) {
-            descriptor.Publish();
+        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 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(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("%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 : writepid_files_) {
+            if (!WriteStringToFile(pid_str, file)) {
+                PLOG(ERROR) << "couldn't write " << pid_str << " to " << file;
+            }
         }
 
-        if (auto result = WritePidToFiles(&writepid_files_); !result) {
-            LOG(ERROR) << "failed to write pid to files: " << result.error();
+        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();
         }
 
         // As requested, set our gid, supplemental gids, uid, context, and
         // priority. Aborts on failure.
-        SetProcessAttributesAndCaps();
+        SetProcessAttributes();
 
         if (!ExpandArgsAndExecv(args_, sigstop_)) {
-            PLOG(ERROR) << "cannot execv('" << args_[0]
-                        << "'). See the 'Debugging init' section of init's README.md for tips";
+            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
         }
 
         _exit(127);
@@ -507,7 +1067,7 @@
         return ErrnoError() << "Failed to fork";
     }
 
-    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
+    if (oom_score_adjust_ != -1000) {
         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)) {
@@ -523,19 +1083,19 @@
 
     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);
+    errno = -createProcessGroup(uid_, pid_, use_memcg);
     if (errno != 0) {
-        PLOG(ERROR) << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
-                    << ") failed for service '" << name_ << "'";
+        PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '"
+                    << name_ << "'";
     } else if (use_memcg) {
         if (swappiness_ != -1) {
-            if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
+            if (!setProcessGroupSwappiness(uid_, pid_, swappiness_)) {
                 PLOG(ERROR) << "setProcessGroupSwappiness failed";
             }
         }
 
         if (soft_limit_in_bytes_ != -1) {
-            if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
+            if (!setProcessGroupSoftLimit(uid_, pid_, soft_limit_in_bytes_)) {
                 PLOG(ERROR) << "setProcessGroupSoftLimit failed";
             }
         }
@@ -562,36 +1122,31 @@
         }
 
         if (computed_limit_in_bytes != size_t(-1)) {
-            if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
+            if (!setProcessGroupLimit(uid_, pid_, computed_limit_in_bytes)) {
                 PLOG(ERROR) << "setProcessGroupLimit failed";
             }
         }
     }
 
-    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
-        LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
-    }
-
     NotifyStateChange("running");
-    reboot_on_failure.Disable();
-    return {};
+    return Success();
 }
 
-Result<void> Service::StartIfNotDisabled() {
+Result<Success> Service::StartIfNotDisabled() {
     if (!(flags_ & SVC_DISABLED)) {
         return Start();
     } else {
         flags_ |= SVC_DISABLED_START;
     }
-    return {};
+    return Success();
 }
 
-Result<void> Service::Enable() {
+Result<Success> Service::Enable() {
     flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
     if (flags_ & SVC_DISABLED_START) {
         return Start();
     }
-    return {};
+    return Success();
 }
 
 void Service::Reset() {
@@ -607,14 +1162,14 @@
     }
 }
 
-Result<void> Service::StartIfPostData() {
+Result<Success> 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 {};
+    return Success();
 }
 
 void Service::Stop() {
@@ -687,8 +1242,37 @@
     }
 }
 
-Result<std::unique_ptr<Service>> Service::MakeTemporaryOneshotService(
-        const std::vector<std::string>& args) {
+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
     std::size_t command_arg = 1;
@@ -699,11 +1283,13 @@
         }
     }
     if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
-        return Error() << "exec called with too many supplementary group ids";
+        LOG(ERROR) << "exec called with too many supplementary group ids";
+        return nullptr;
     }
 
     if (command_arg >= args.size()) {
-        return Error() << "exec called without command";
+        LOG(ERROR) << "exec called without command";
+        return nullptr;
     }
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
@@ -722,7 +1308,8 @@
     if (command_arg > 3) {
         uid = DecodeUid(args[2]);
         if (!uid) {
-            return Error() << "Unable to decode UID for '" << args[2] << "': " << uid.error();
+            LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
+            return nullptr;
         }
     }
     Result<gid_t> gid = 0;
@@ -730,14 +1317,16 @@
     if (command_arg > 4) {
         gid = DecodeUid(args[3]);
         if (!gid) {
-            return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
+            LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
+            return nullptr;
         }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
             auto supp_gid = DecodeUid(args[4 + i]);
             if (!supp_gid) {
-                return Error() << "Unable to decode GID for '" << args[4 + i]
-                               << "': " << supp_gid.error();
+                LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
+                           << "': " << supp_gid.error();
+                return nullptr;
             }
             supp_gids.push_back(*supp_gid);
         }
@@ -747,5 +1336,139 @@
                                      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();
+    }
+}
+
+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_string();
+        }
+    }
+    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());
+}
+
+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 << "'";
+    }
+
+    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 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()
+                               << "'";
+            }
+
+            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 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;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/service.h b/init/service.h
index 272c9f9..ae29f28 100644
--- a/init/service.h
+++ b/init/service.h
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_SERVICE_H
+#define _INIT_SERVICE_H
 
 #include <signal.h>
+#include <sys/resource.h>
 #include <sys/types.h>
 
 #include <chrono>
@@ -31,9 +33,9 @@
 
 #include "action.h"
 #include "capabilities.h"
+#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
@@ -61,26 +63,24 @@
 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, int namespace_flags, const std::string& seclabel,
-            Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
-
-    static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
+            const std::vector<gid_t>& supp_gids, unsigned 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; }
-    bool IsEnabled() { return (flags_ & SVC_DISABLED) == 0; }
-    Result<void> ExecStart();
-    Result<void> Start();
-    Result<void> StartIfNotDisabled();
-    Result<void> StartIfPostData();
-    Result<void> Enable();
+    Result<Success> ParseLine(std::vector<std::string>&& args);
+    Result<Success> ExecStart();
+    Result<Success> Start();
+    Result<Success> StartIfNotDisabled();
+    Result<Success> StartIfPostData();
+    Result<Success> Enable();
     void Reset();
     void ResetIfPostData();
     void Stop();
@@ -98,7 +98,6 @@
     void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
         reap_callbacks_.emplace_back(std::move(callback));
     }
-    size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
 
     static bool is_exec_service_running() { return is_exec_service_running_; }
 
@@ -108,16 +107,16 @@
     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 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; }
+    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_; }
     const std::string& seclabel() const { return seclabel_; }
     const std::vector<int>& keycodes() const { return keycodes_; }
-    IoSchedClass ioprio_class() const { return proc_attr_.ioprio_class; }
-    int ioprio_pri() const { return proc_attr_.ioprio_pri; }
+    IoSchedClass ioprio_class() const { return ioprio_class_; }
+    int ioprio_pri() const { return ioprio_pri_; }
     const std::set<std::string>& interfaces() const { return interfaces_; }
-    int priority() const { return proc_attr_.priority; }
+    int priority() const { return priority_; }
     int oom_score_adjust() const { return oom_score_adjust_; }
     bool is_override() const { return override_; }
     bool process_cgroup_empty() const { return process_cgroup_empty_; }
@@ -130,16 +129,62 @@
     bool is_post_data() const { return post_data_; }
 
   private:
+    using OptionParser = Result<Success> (Service::*)(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 SetProcessAttributesAndCaps();
+    void SetProcessAttributes();
+
+    Result<Success> ParseCapabilities(std::vector<std::string>&& args);
+    Result<Success> ParseClass(std::vector<std::string>&& args);
+    Result<Success> ParseConsole(std::vector<std::string>&& args);
+    Result<Success> ParseCritical(std::vector<std::string>&& args);
+    Result<Success> ParseDisabled(std::vector<std::string>&& args);
+    Result<Success> ParseEnterNamespace(std::vector<std::string>&& args);
+    Result<Success> ParseGroup(std::vector<std::string>&& args);
+    Result<Success> ParsePriority(std::vector<std::string>&& args);
+    Result<Success> ParseInterface(std::vector<std::string>&& args);
+    Result<Success> ParseIoprio(std::vector<std::string>&& args);
+    Result<Success> ParseKeycodes(std::vector<std::string>&& args);
+    Result<Success> ParseOneshot(std::vector<std::string>&& args);
+    Result<Success> ParseOnrestart(std::vector<std::string>&& args);
+    Result<Success> ParseOomScoreAdjust(std::vector<std::string>&& args);
+    Result<Success> ParseOverride(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitInBytes(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitPercent(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgLimitProperty(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);
+    Result<Success> ParseMemcgSwappiness(std::vector<std::string>&& args);
+    Result<Success> ParseNamespace(std::vector<std::string>&& args);
+    Result<Success> ParseProcessRlimit(std::vector<std::string>&& args);
+    Result<Success> ParseRestartPeriod(std::vector<std::string>&& args);
+    Result<Success> ParseSeclabel(std::vector<std::string>&& args);
+    Result<Success> ParseSetenv(std::vector<std::string>&& args);
+    Result<Success> ParseShutdown(std::vector<std::string>&& args);
+    Result<Success> ParseSigstop(std::vector<std::string>&& args);
+    Result<Success> ParseSocket(std::vector<std::string>&& args);
+    Result<Success> ParseTimeoutPeriod(std::vector<std::string>&& args);
+    Result<Success> ParseFile(std::vector<std::string>&& args);
+    Result<Success> ParseUser(std::vector<std::string>&& args);
+    Result<Success> ParseWritepid(std::vector<std::string>&& args);
+    Result<Success> ParseUpdatable(std::vector<std::string>&& args);
+
+    template <typename T>
+    Result<Success> AddDescriptor(std::vector<std::string>&& args);
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
 
     std::string name_;
     std::set<std::string> classnames_;
+    std::string console_;
 
     unsigned flags_;
     pid_t pid_;
@@ -147,14 +192,17 @@
     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_;
     std::optional<CapSet> capabilities_;
-    ProcessAttributes proc_attr_;
-    NamespaceInfo namespaces_;
+    unsigned namespace_flags_;
+    // Pair of namespace type, path to namespace.
+    std::vector<std::pair<int, std::string>> namespaces_to_enter_;
 
     std::string seclabel_;
 
-    std::vector<SocketDescriptor> sockets_;
-    std::vector<FileDescriptor> files_;
+    std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
     std::vector<std::pair<std::string, std::string>> environment_vars_;
 
     Action onrestart_;  // Commands to execute on restart.
@@ -166,6 +214,10 @@
     // keycodes for triggering this service via /dev/input/input*
     std::vector<int> keycodes_;
 
+    IoSchedClass ioprio_class_;
+    int ioprio_pri_;
+    int priority_;
+
     int oom_score_adjust_;
 
     int swappiness_ = -1;
@@ -181,6 +233,8 @@
 
     unsigned long start_order_;
 
+    std::vector<std::pair<int, rlimit>> rlimits_;
+
     bool sigstop_ = false;
 
     std::chrono::seconds restart_period_ = 5s;
@@ -197,9 +251,81 @@
     bool post_data_ = false;
 
     bool running_at_post_data_reset_ = false;
+};
 
-    std::optional<std::string> on_failure_reboot_target_;
+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_;
+};
+
+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;
+    void EndFile() override { filename_ = ""; }
+
+  private:
+    bool IsValidName(const std::string& name) const;
+
+    ServiceList* service_list_;
+    std::vector<Subcontext>* subcontexts_;
+    std::unique_ptr<Service> service_;
+    std::string filename_;
 };
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/service_list.cpp b/init/service_list.cpp
deleted file mode 100644
index c51a9cf..0000000
--- a/init/service_list.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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;
-}
-
-size_t ServiceList::CheckAllCommands() {
-    size_t failures = 0;
-    for (const auto& service : services_) {
-        failures += service->CheckAllCommands();
-    }
-    return failures;
-}
-
-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
deleted file mode 100644
index ee2c702..0000000
--- a/init/service_list.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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();
-    size_t CheckAllCommands();
-
-    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
deleted file mode 100644
index 154d1dd..0000000
--- a/init/service_parser.cpp
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * 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 <stdlib.h>
-#include <sys/socket.h>
-
-#include <algorithm>
-#include <sstream>
-
-#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 "lmkd_service.h"
-#include "rlimit_parser.h"
-#include "service_utils.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) {
-    if (service_->proc_attr_.stdio_to_kmsg) {
-        return Error() << "'console' and 'stdio_to_kmsg' are mutually exclusive";
-    }
-    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];
-
-    // AIDL services don't use fully qualified names and instead just use "interface aidl <name>"
-    if (interface_name != "aidl") {
-        FQName fq_name;
-        if (!FQName::parse(interface_name, &fq_name)) {
-            return Error() << "Invalid fully-qualified name for interface '" << interface_name
-                           << "'";
-        }
-
-        if (!fq_name.isFullyQualified()) {
-            return Error() << "Interface name not fully-qualified '" << interface_name << "'";
-        }
-
-        if (fq_name.isValidValueName()) {
-            return Error() << "Interface name must not be a value name '" << interface_name << "'";
-        }
-    }
-
-    const std::string fullname = interface_name + "/" + instance_name;
-
-    for (const auto& svc : *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], "$")) {
-        auto expanded = ExpandProps(args[1]);
-        if (!expanded) {
-            return expanded.error();
-        }
-
-        // 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_, MIN_OOM_SCORE_ADJUST,
-                  MAX_OOM_SCORE_ADJUST)) {
-        return Error() << "oom_score_adjust value must be in range " << MIN_OOM_SCORE_ADJUST
-                       << " - +" << MAX_OOM_SCORE_ADJUST;
-    }
-    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::ParseRebootOnFailure(std::vector<std::string>&& args) {
-    if (service_->on_failure_reboot_target_) {
-        return Error() << "Only one reboot_on_failure command may be specified";
-    }
-    if (!StartsWith(args[1], "shutdown") && !StartsWith(args[1], "reboot")) {
-        return Error()
-               << "reboot_on_failure commands must begin with either 'shutdown' or 'reboot'";
-    }
-    service_->on_failure_reboot_target_ = std::move(args[1]);
-    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 {};
-}
-
-// name type perm [ uid gid context ]
-Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
-    SocketDescriptor socket;
-    socket.name = std::move(args[1]);
-
-    auto types = Split(args[2], "+");
-    if (types[0] == "stream") {
-        socket.type = SOCK_STREAM;
-    } else if (types[0] == "dgram") {
-        socket.type = SOCK_DGRAM;
-    } else if (types[0] == "seqpacket") {
-        socket.type = SOCK_SEQPACKET;
-    } else {
-        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket', got '" << types[0]
-                       << "' instead.";
-    }
-
-    if (types.size() > 1) {
-        if (types.size() == 2 && types[1] == "passcred") {
-            socket.passcred = true;
-        } else {
-            return Error() << "Only 'passcred' may be used to modify the socket type";
-        }
-    }
-
-    errno = 0;
-    char* end = nullptr;
-    socket.perm = strtol(args[3].c_str(), &end, 8);
-    if (errno != 0) {
-        return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
-    }
-    if (end == args[3].c_str() || *end != '\0') {
-        errno = EINVAL;
-        return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
-    }
-
-    if (args.size() > 4) {
-        auto uid = DecodeUid(args[4]);
-        if (!uid) {
-            return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
-        }
-        socket.uid = *uid;
-    }
-
-    if (args.size() > 5) {
-        auto gid = DecodeUid(args[5]);
-        if (!gid) {
-            return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
-        }
-        socket.gid = *gid;
-    }
-
-    socket.context = args.size() > 6 ? args[6] : "";
-
-    auto old = std::find_if(service_->sockets_.begin(), service_->sockets_.end(),
-                            [&socket](const auto& other) { return socket.name == other.name; });
-
-    if (old != service_->sockets_.end()) {
-        return Error() << "duplicate socket descriptor '" << socket.name << "'";
-    }
-
-    service_->sockets_.emplace_back(std::move(socket));
-
-    return {};
-}
-
-Result<void> ServiceParser::ParseStdioToKmsg(std::vector<std::string>&& args) {
-    if (service_->flags_ & SVC_CONSOLE) {
-        return Error() << "'stdio_to_kmsg' and 'console' are mutually exclusive";
-    }
-    service_->proc_attr_.stdio_to_kmsg = true;
-    return {};
-}
-
-// name type
-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'";
-    }
-
-    FileDescriptor file;
-    file.type = args[2];
-
-    auto file_name = ExpandProps(args[1]);
-    if (!file_name) {
-        return Error() << "Could not expand file path ': " << file_name.error();
-    }
-    file.name = *file_name;
-    if (file.name[0] != '/' || file.name.find("../") != std::string::npos) {
-        return Error() << "file name must not be relative";
-    }
-
-    auto old = std::find_if(service_->files_.begin(), service_->files_.end(),
-                            [&file](const auto& other) { return other.name == file.name; });
-
-    if (old != service_->files_.end()) {
-        return Error() << "duplicate file descriptor '" << file.name << "'";
-    }
-
-    service_->files_.emplace_back(std::move(file));
-
-    return {};
-}
-
-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 {};
-}
-
-const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() const {
-    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
-    // clang-format off
-    static const KeywordMap<ServiceParser::OptionParser> parser_map = {
-        {"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}},
-        {"reboot_on_failure",       {1,     1,    &ServiceParser::ParseRebootOnFailure}},
-        {"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}},
-        {"stdio_to_kmsg",           {0,     0,    &ServiceParser::ParseStdioToKmsg}},
-        {"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 parser_map;
-}
-
-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 (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
-        restart_action_subcontext = subcontext_;
-    }
-
-    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";
-        }
-    }
-    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
-        if (str_args[0] == "/charger") {
-            str_args[0] = "/system/bin/charger";
-        }
-    }
-
-    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 {};
-    }
-
-    auto parser = GetParserMap().Find(args);
-
-    if (!parser) return parser.error();
-
-    return std::invoke(*parser, this, std::move(args));
-}
-
-Result<void> ServiceParser::EndSection() {
-    if (!service_) {
-        return {};
-    }
-
-    if (interface_inheritance_hierarchy_) {
-        if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy(
-                    service_->interfaces(), *interface_inheritance_hierarchy_);
-            !check_hierarchy_result) {
-            return Error() << check_hierarchy_result.error();
-        }
-    }
-
-    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
deleted file mode 100644
index b1281f5..0000000
--- a/init/service_parser.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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 "interface_utils.h"
-#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, Subcontext* subcontext,
-            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
-        : service_list_(service_list),
-          subcontext_(subcontext),
-          interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
-          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);
-    const KeywordMap<ServiceParser::OptionParser>& GetParserMap() const;
-
-    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> ParseRebootOnFailure(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> ParseStdioToKmsg(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);
-
-    bool IsValidName(const std::string& name) const;
-
-    ServiceList* service_list_;
-    Subcontext* subcontext_;
-    std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
-    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 c158b0a..4bfaa6b 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -23,7 +23,6 @@
 
 #include <gtest/gtest.h>
 
-#include "lmkd_service.h"
 #include "util.h"
 
 namespace android {
@@ -31,7 +30,7 @@
 
 TEST(service, pod_initialized) {
     constexpr auto memory_size = sizeof(Service);
-    alignas(alignof(Service)) unsigned char old_memory[memory_size];
+    alignas(alignof(Service)) char old_memory[memory_size];
 
     for (std::size_t i = 0; i < memory_size; ++i) {
         old_memory[i] = 0xFF;
@@ -46,11 +45,11 @@
     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(0, service_in_old_memory->namespace_flags());
+    EXPECT_EQ(0U, 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());
-    EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory->oom_score_adjust());
+    EXPECT_EQ(-1000, service_in_old_memory->oom_score_adjust());
     EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 
     for (std::size_t i = 0; i < memory_size; ++i) {
@@ -65,26 +64,26 @@
     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(0, service_in_old_memory2->namespace_flags());
+    EXPECT_EQ(0U, 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());
-    EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory2->oom_score_adjust());
+    EXPECT_EQ(-1000, service_in_old_memory2->oom_score_adjust());
     EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
 }
 
 TEST(service, make_temporary_oneshot_service_invalid_syntax) {
     std::vector<std::string> args;
     // Nothing.
-    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
 
     // No arguments to 'exec'.
     args.push_back("exec");
-    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
 
     // No command in "exec --".
     args.push_back("--");
-    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
 }
 
 TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
@@ -98,7 +97,7 @@
     }
     args.push_back("--");
     args.push_back("/system/bin/id");
-    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
+    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
 }
 
 static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
@@ -123,9 +122,8 @@
     }
     args.push_back("/system/bin/toybox");
     args.push_back("id");
-    auto service_ret = Service::MakeTemporaryOneshotService(args);
-    ASSERT_TRUE(service_ret);
-    auto svc = std::move(*service_ret);
+    auto svc = Service::MakeTemporaryOneshotService(args);
+    ASSERT_NE(nullptr, svc);
 
     if (seclabel) {
         ASSERT_EQ("u:r:su:s0", svc->seclabel());
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
deleted file mode 100644
index 93cffd8..0000000
--- a/init/service_utils.cpp
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * 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 <fcntl.h>
-#include <grp.h>
-#include <sys/mount.h>
-#include <sys/prctl.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <cutils/android_get_control_file.h>
-#include <cutils/sockets.h>
-#include <processgroup/processgroup.h>
-
-#include "mount_namespace.h"
-#include "util.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 SetupStdio(bool stdio_to_kmsg) {
-    auto fd = unique_fd{open("/dev/null", O_RDWR | O_CLOEXEC)};
-    dup2(fd, STDIN_FILENO);
-    if (stdio_to_kmsg) {
-        fd.reset(open("/dev/kmsg_debug", O_WRONLY | O_CLOEXEC));
-        if (fd == -1) fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
-    }
-    dup2(fd, STDOUT_FILENO);
-    dup2(fd, STDERR_FILENO);
-}
-
-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
-
-void Descriptor::Publish() const {
-    auto published_name = name_;
-
-    for (auto& c : published_name) {
-        c = isalnum(c) ? c : '_';
-    }
-
-    int fd = fd_.get();
-    // For safety, the FD is created as CLOEXEC, so that must be removed before publishing.
-    auto fd_flags = fcntl(fd, F_GETFD);
-    fd_flags &= ~FD_CLOEXEC;
-    if (fcntl(fd, F_SETFD, fd_flags) != 0) {
-        PLOG(ERROR) << "Failed to remove CLOEXEC from '" << published_name << "'";
-    }
-
-    std::string val = std::to_string(fd);
-    setenv(published_name.c_str(), val.c_str(), 1);
-}
-
-Result<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {
-    const auto& socket_context = context.empty() ? global_context : context;
-    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, perm, uid, gid, socket_context);
-    if (!result) {
-        return result.error();
-    }
-
-    return Descriptor(ANDROID_SOCKET_ENV_PREFIX + name, unique_fd(*result));
-}
-
-Result<Descriptor> FileDescriptor::Create() const {
-    int flags = (type == "r") ? O_RDONLY : (type == "w") ? O_WRONLY : O_RDWR;
-
-    // Make sure we do not block on open (eg: devices can chose to block on carrier detect).  Our
-    // intention is never to delay launch of a service for such a condition.  The service can
-    // perform its own blocking on carrier detect.
-    unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK | O_CLOEXEC)));
-
-    if (fd < 0) {
-        return ErrnoError() << "Failed to open file '" << name << "'";
-    }
-
-    // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
-    fcntl(fd, F_SETFL, flags);
-
-    LOG(INFO) << "Opened file '" << name << "', flags " << flags;
-
-    return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
-}
-
-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";
-        }
-        SetupStdio(attr.stdio_to_kmsg);
-    }
-
-    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
deleted file mode 100644
index 3f1071e..0000000
--- a/init/service_utils.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 <android-base/unique_fd.h>
-#include <cutils/iosched_policy.h>
-
-#include "result.h"
-
-namespace android {
-namespace init {
-
-class Descriptor {
-  public:
-    Descriptor(const std::string& name, android::base::unique_fd fd)
-        : name_(name), fd_(std::move(fd)){};
-
-    void Publish() const;
-
-  private:
-    std::string name_;
-    android::base::unique_fd fd_;
-};
-
-struct SocketDescriptor {
-    std::string name;
-    int type = 0;
-    uid_t uid = 0;
-    gid_t gid = 0;
-    int perm = 0;
-    std::string context;
-    bool passcred = false;
-
-    Result<Descriptor> Create(const std::string& global_context) const;
-};
-
-struct FileDescriptor {
-    std::string name;
-    std::string type;
-
-    Result<Descriptor> Create() const;
-};
-
-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;
-    bool stdio_to_kmsg;
-};
-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 9b2c7d9..0b03324 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -28,31 +28,28 @@
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 
-#include <thread>
-
 #include "init.h"
+#include "property_service.h"
 #include "service.h"
-#include "service_list.h"
 
+using android::base::StringPrintf;
 using android::base::boot_clock;
 using android::base::make_scope_guard;
-using android::base::StringPrintf;
-using android::base::Timer;
 
 namespace android {
 namespace init {
 
-static pid_t ReapOneProcess() {
+static bool ReapOneProcess() {
     siginfo_t siginfo = {};
     // This returns a zombie pid or informs us that there are no zombies left to be reaped.
     // It does NOT reap the pid; that is done below.
     if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
         PLOG(ERROR) << "waitid failed";
-        return 0;
+        return false;
     }
 
     auto pid = siginfo.si_pid;
-    if (pid == 0) return 0;
+    if (pid == 0) return false;
 
     // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
     // whenever the function returns from this point forward.
@@ -64,7 +61,9 @@
     std::string wait_string;
     Service* service = nullptr;
 
-    if (SubcontextChildReap(pid)) {
+    if (PropertyChildReap(pid)) {
+        name = "Async property child";
+    } else if (SubcontextChildReap(pid)) {
         name = "Subcontext";
     } else {
         service = ServiceList::GetInstance().FindService(pid, &Service::pid);
@@ -76,13 +75,6 @@
                 auto exec_duration_ms =
                     std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
                 wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
-            } else if (service->flags() & SVC_ONESHOT) {
-                auto exec_duration = boot_clock::now() - service->time_started();
-                auto exec_duration_ms =
-                        std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
-                                .count();
-                wait_string = StringPrintf(" oneshot service took %f seconds in background",
-                                           exec_duration_ms / 1000.0f);
             }
         } else {
             name = StringPrintf("Untracked pid %d", pid);
@@ -95,7 +87,7 @@
         LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
     }
 
-    if (!service) return pid;
+    if (!service) return true;
 
     service->Reap(siginfo);
 
@@ -103,33 +95,13 @@
         ServiceList::GetInstance().RemoveService(*service);
     }
 
-    return pid;
+    return true;
 }
 
 void ReapAnyOutstandingChildren() {
-    while (ReapOneProcess() != 0) {
+    while (ReapOneProcess()) {
     }
 }
 
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout) {
-    Timer t;
-    std::vector<pid_t> alive_pids(pids.begin(), pids.end());
-    while (!alive_pids.empty() && t.duration() < timeout) {
-        pid_t pid;
-        while ((pid = ReapOneProcess()) != 0) {
-            auto it = std::find(alive_pids.begin(), alive_pids.end(), pid);
-            if (it != alive_pids.end()) {
-                alive_pids.erase(it);
-            }
-        }
-        if (alive_pids.empty()) {
-            break;
-        }
-        std::this_thread::sleep_for(50ms);
-    }
-    LOG(INFO) << "Waiting for " << pids.size() << " pids to be reaped took " << t << " with "
-              << alive_pids.size() << " of them still running";
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
index fac1020..30063f2 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -17,16 +17,11 @@
 #ifndef _INIT_SIGCHLD_HANDLER_H_
 #define _INIT_SIGCHLD_HANDLER_H_
 
-#include <chrono>
-#include <vector>
-
 namespace android {
 namespace init {
 
 void ReapAnyOutstandingChildren();
 
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout);
-
 }  // namespace init
 }  // namespace android
 
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 79fc372..092c51c 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -18,23 +18,20 @@
 
 #include <fcntl.h>
 #include <poll.h>
+#include <sys/socket.h>
 #include <unistd.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <selinux/android.h>
 
 #include "action.h"
-#include "builtins.h"
-#include "proto_utils.h"
 #include "util.h"
 
 #if defined(__ANDROID__)
 #include <android/api-level.h>
 #include "property_service.h"
-#include "selabel.h"
 #include "selinux.h"
 #else
 #include "host_init_stubs.h"
@@ -49,11 +46,59 @@
 
 namespace android {
 namespace init {
+
+const std::string kInitContext = "u:r:init:s0";
+const std::string kVendorContext = "u:r:vendor_init:s0";
+
+const char* const paths_and_secontexts[2][2] = {
+    {"/vendor", kVendorContext.c_str()},
+    {"/odm", kVendorContext.c_str()},
+};
+
 namespace {
 
+constexpr size_t kBufferSize = 4096;
+
+Result<std::string> ReadMessage(int socket) {
+    char buffer[kBufferSize] = {};
+    auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
+    if (result == 0) {
+        return Error();
+    } else if (result < 0) {
+        return ErrnoError();
+    }
+    return std::string(buffer, result);
+}
+
+template <typename T>
+Result<Success> SendMessage(int socket, const T& message) {
+    std::string message_string;
+    if (!message.SerializeToString(&message_string)) {
+        return Error() << "Unable to serialize message";
+    }
+
+    if (message_string.size() > kBufferSize) {
+        return Error() << "Serialized message too long to send";
+    }
+
+    if (auto result =
+            TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
+        result != static_cast<long>(message_string.size())) {
+        return ErrnoError() << "send() failed to send message contents";
+    }
+    return Success();
+}
+
+std::vector<std::pair<std::string, std::string>> properties_to_set;
+
+uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
+    properties_to_set.emplace_back(name, value);
+    return 0;
+}
+
 class SubcontextProcess {
   public:
-    SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)
+    SubcontextProcess(const KeywordFunctionMap* function_map, std::string context, int init_fd)
         : function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};
     void MainLoop();
 
@@ -63,7 +108,7 @@
     void ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
                     SubcontextReply* reply) const;
 
-    const BuiltinFunctionMap* function_map_;
+    const KeywordFunctionMap* function_map_;
     const std::string context_;
     const int init_fd_;
 };
@@ -76,35 +121,43 @@
         args.emplace_back(string);
     }
 
-    auto map_result = function_map_->Find(args);
-    Result<void> result;
+    auto map_result = function_map_->FindFunction(args);
+    Result<Success> result;
     if (!map_result) {
         result = Error() << "Cannot find command: " << map_result.error();
     } else {
-        result = RunBuiltinFunction(map_result->function, args, context_);
+        result = RunBuiltinFunction(map_result->second, args, context_);
     }
 
+    for (const auto& [name, value] : properties_to_set) {
+        auto property = reply->add_properties_to_set();
+        property->set_name(name);
+        property->set_value(value);
+    }
+
+    properties_to_set.clear();
+
     if (result) {
         reply->set_success(true);
     } else {
         auto* failure = reply->mutable_failure();
-        failure->set_error_string(result.error().message());
-        failure->set_error_errno(result.error().code());
+        failure->set_error_string(result.error_string());
+        failure->set_error_errno(result.error_errno());
     }
 }
 
 void SubcontextProcess::ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
                                    SubcontextReply* reply) const {
     for (const auto& arg : expand_args_command.args()) {
-        auto expanded_arg = ExpandProps(arg);
-        if (!expanded_arg) {
+        auto expanded_prop = std::string{};
+        if (!expand_props(arg, &expanded_prop)) {
             auto* failure = reply->mutable_failure();
-            failure->set_error_string(expanded_arg.error().message());
+            failure->set_error_string("Failed to expand '" + arg + "'");
             failure->set_error_errno(0);
             return;
         } else {
             auto* expand_args_reply = reply->mutable_expand_args_reply();
-            expand_args_reply->add_expanded_args(*expanded_arg);
+            expand_args_reply->add_expanded_args(expanded_prop);
         }
     }
 }
@@ -124,7 +177,7 @@
 
         auto init_message = ReadMessage(init_fd_);
         if (!init_message) {
-            if (init_message.error().code() == 0) {
+            if (init_message.error_errno() == 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.
@@ -161,7 +214,7 @@
 
 }  // namespace
 
-int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map) {
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map) {
     if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")";
 
     auto context = std::string(argv[2]);
@@ -169,10 +222,7 @@
 
     SelabelInitialize();
 
-    property_set = [](const std::string& key, const std::string& value) -> uint32_t {
-        android::base::SetProperty(key, value);
-        return 0;
-    };
+    property_set = SubcontextPropertySet;
 
     auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
     subcontext_process.MainLoop();
@@ -195,7 +245,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);  // NOLINT(android-cloexec-dup)
+        int child_fd = dup(subcontext_socket);
         if (child_fd < 0) {
             PLOG(FATAL) << "Could not dup child_fd";
         }
@@ -228,15 +278,6 @@
     Fork();
 }
 
-bool Subcontext::PathMatchesSubcontext(const std::string& path) {
-    for (const auto& prefix : path_prefixes_) {
-        if (StartsWith(path, prefix)) {
-            return true;
-        }
-    }
-    return false;
-}
-
 Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
     if (auto result = SendMessage(socket_, subcontext_command); !result) {
         Restart();
@@ -257,7 +298,7 @@
     return subcontext_reply;
 }
 
-Result<void> Subcontext::Execute(const std::vector<std::string>& args) {
+Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
     auto subcontext_command = SubcontextCommand();
     std::copy(
         args.begin(), args.end(),
@@ -268,6 +309,15 @@
         return subcontext_reply.error();
     }
 
+    for (const auto& property : subcontext_reply->properties_to_set()) {
+        ucred cr = {.pid = pid_, .uid = 0, .gid = 0};
+        std::string error;
+        if (HandlePropertySet(property.name(), property.value(), context_, cr, &error) != 0) {
+            LOG(ERROR) << "Subcontext init could not set '" << property.name() << "' to '"
+                       << property.value() << "': " << error;
+        }
+    }
+
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
         auto& failure = subcontext_reply->failure();
         return ResultError(failure.error_string(), failure.error_errno());
@@ -278,7 +328,7 @@
                        << subcontext_reply->reply_case();
     }
 
-    return {};
+    return Success();
 }
 
 Result<std::vector<std::string>> Subcontext::ExpandArgs(const std::vector<std::string>& args) {
@@ -313,12 +363,13 @@
 static std::vector<Subcontext> subcontexts;
 static bool shutting_down;
 
-std::unique_ptr<Subcontext> InitializeSubcontext() {
+std::vector<Subcontext>* InitializeSubcontexts() {
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
-                                            kVendorContext);
+        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+            subcontexts.emplace_back(path_prefix, secontext);
+        }
     }
-    return nullptr;
+    return &subcontexts;
 }
 
 bool SubcontextChildReap(pid_t pid) {
diff --git a/init/subcontext.h b/init/subcontext.h
index bcaad29..628fd50 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_SUBCONTEXT_H
+#define _INIT_SUBCONTEXT_H
 
 #include <signal.h>
 
@@ -30,21 +31,22 @@
 namespace android {
 namespace init {
 
-static constexpr const char kInitContext[] = "u:r:init:s0";
-static constexpr const char kVendorContext[] = "u:r:vendor_init:s0";
+extern const std::string kInitContext;
+extern const std::string kVendorContext;
+extern const char* const paths_and_secontexts[2][2];
 
 class Subcontext {
   public:
-    Subcontext(std::vector<std::string> path_prefixes, std::string context)
-        : path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
+    Subcontext(std::string path_prefix, std::string context)
+        : path_prefix_(std::move(path_prefix)), context_(std::move(context)), pid_(0) {
         Fork();
     }
 
-    Result<void> Execute(const std::vector<std::string>& args);
+    Result<Success> Execute(const std::vector<std::string>& args);
     Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
     void Restart();
-    bool PathMatchesSubcontext(const std::string& path);
 
+    const std::string& path_prefix() const { return path_prefix_; }
     const std::string& context() const { return context_; }
     pid_t pid() const { return pid_; }
 
@@ -52,16 +54,18 @@
     void Fork();
     Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
 
-    std::vector<std::string> path_prefixes_;
+    std::string path_prefix_;
     std::string context_;
     pid_t pid_;
     android::base::unique_fd socket_;
 };
 
-int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::unique_ptr<Subcontext> InitializeSubcontext();
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
+std::vector<Subcontext>* InitializeSubcontexts();
 bool SubcontextChildReap(pid_t pid);
 void SubcontextTerminate();
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/subcontext.proto b/init/subcontext.proto
index e68115e..c31f4fb 100644
--- a/init/subcontext.proto
+++ b/init/subcontext.proto
@@ -38,4 +38,10 @@
         Failure failure = 2;
         ExpandArgsReply expand_args_reply = 3;
     }
+
+    message PropertyToSet {
+        optional string name = 1;
+        optional string value = 2;
+    }
+    repeated PropertyToSet properties_to_set = 4;
 }
\ No newline at end of file
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index ccef2f3..eae03e3 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -19,6 +19,8 @@
 #include <benchmark/benchmark.h>
 #include <selinux/selinux.h>
 
+#include "test_function_map.h"
+
 namespace android {
 namespace init {
 
@@ -33,11 +35,11 @@
         return;
     }
 
-    auto subcontext = Subcontext({"path"}, context);
+    auto subcontext = Subcontext("path", context);
     free(context);
 
     while (state.KeepRunning()) {
-        subcontext.Execute(std::vector<std::string>{"return_success"});
+        subcontext.Execute(std::vector<std::string>{"return_success"}).IgnoreError();
     }
 
     if (subcontext.pid() > 0) {
@@ -48,11 +50,11 @@
 
 BENCHMARK(BenchmarkSuccess);
 
-BuiltinFunctionMap BuildTestFunctionMap() {
-    auto function = [](const BuiltinArguments& args) { return Result<void>{}; };
-    BuiltinFunctionMap test_function_map = {
-            {"return_success", {0, 0, {true, function}}},
-    };
+TestFunctionMap BuildTestFunctionMap() {
+    TestFunctionMap test_function_map;
+    test_function_map.Add("return_success", 0, 0, true,
+                          [](const BuiltinArguments& args) { return Success(); });
+
     return test_function_map;
 }
 
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 9cac35e..230203a 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -26,6 +26,7 @@
 #include <selinux/selinux.h>
 
 #include "builtin_arguments.h"
+#include "test_function_map.h"
 
 using namespace std::literals;
 
@@ -43,7 +44,7 @@
 template <typename F>
 void RunTest(F&& test_function) {
     if (getuid() != 0) {
-        GTEST_SKIP() << "Skipping test, must be run as root.";
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
         return;
     }
 
@@ -52,7 +53,7 @@
     auto context_string = std::string(context);
     free(context);
 
-    auto subcontext = Subcontext({"dummy_path"}, context_string);
+    auto subcontext = Subcontext("dummy_path", context_string);
     ASSERT_NE(0, subcontext.pid());
 
     test_function(subcontext, context_string);
@@ -68,7 +69,7 @@
         auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
         ASSERT_FALSE(result);
 
-        auto pids = Split(result.error().message(), " ");
+        auto pids = Split(result.error_string(), " ");
         ASSERT_EQ(2U, pids.size());
         auto our_pid = std::to_string(getpid());
         EXPECT_NE(our_pid, pids[0]);
@@ -115,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().message());
+        EXPECT_EQ(Join(expected_words, " "), result.error_string());
         EXPECT_EQ(first_pid, subcontext.pid());
     });
 }
@@ -129,7 +130,7 @@
 
         auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
         ASSERT_FALSE(result2);
-        EXPECT_EQ("Sane error!", result2.error().message());
+        EXPECT_EQ("Sane error!", result2.error_string());
         EXPECT_NE(subcontext.pid(), first_pid);
     });
 }
@@ -138,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().message());
+        ASSERT_EQ(context_string, result.error_string());
     });
 }
 
@@ -166,66 +167,62 @@
         };
         auto result = subcontext.ExpandArgs(args);
         ASSERT_FALSE(result);
-        EXPECT_EQ("unexpected end of string in '" + args[1] + "', looking for }",
-                  result.error().message());
+        EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error_string());
     });
 }
 
-BuiltinFunctionMap BuildTestFunctionMap() {
+TestFunctionMap BuildTestFunctionMap() {
+    TestFunctionMap test_function_map;
     // For CheckDifferentPid
-    auto do_return_pids_as_error = [](const BuiltinArguments& args) -> Result<void> {
-        return Error() << getpid() << " " << getppid();
-    };
+    test_function_map.Add("return_pids_as_error", 0, 0, true,
+                          [](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << getpid() << " " << getppid();
+                          });
 
     // For SetProp
-    auto do_setprop = [](const BuiltinArguments& args) {
+    test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) {
         android::base::SetProperty(args[1], args[2]);
-        return Result<void>{};
-    };
+        return Success();
+    });
 
     // For MultipleCommands
     // Using a shared_ptr to extend lifetime of words to both lambdas
     auto words = std::make_shared<std::vector<std::string>>();
-    auto do_add_word = [words](const BuiltinArguments& args) {
+    test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) {
         words->emplace_back(args[1]);
-        return Result<void>{};
-    };
-    auto do_return_words_as_error = [words](const BuiltinArguments& args) -> Result<void> {
-        return Error() << Join(*words, " ");
-    };
+        return Success();
+    });
+    test_function_map.Add("return_words_as_error", 0, 0, true,
+                          [words](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << Join(*words, " ");
+                          });
 
     // For RecoverAfterAbort
-    auto do_cause_log_fatal = [](const BuiltinArguments& args) -> Result<void> {
-        return Error() << std::string(4097, 'f');
-    };
-    auto do_generate_sane_error = [](const BuiltinArguments& args) -> Result<void> {
-        return Error() << "Sane error!";
-    };
+    test_function_map.Add("cause_log_fatal", 0, 0, true,
+                          [](const BuiltinArguments& args) -> Result<Success> {
+                              return Error() << std::string(4097, 'f');
+                          });
+    test_function_map.Add(
+        "generate_sane_error", 0, 0, true,
+        [](const BuiltinArguments& args) -> Result<Success> { return Error() << "Sane error!"; });
 
     // For ContextString
-    auto do_return_context_as_error = [](const BuiltinArguments& args) -> Result<void> {
-        return Error() << args.context;
-    };
+    test_function_map.Add(
+        "return_context_as_error", 0, 0, true,
+        [](const BuiltinArguments& args) -> Result<Success> { return Error() << args.context; });
 
-    // clang-format off
-    BuiltinFunctionMap test_function_map = {
-        {"return_pids_as_error",        {0,     0,      {true,  do_return_pids_as_error}}},
-        {"setprop",                     {2,     2,      {true,  do_setprop}}},
-        {"add_word",                    {1,     1,      {true,  do_add_word}}},
-        {"return_words_as_error",       {0,     0,      {true,  do_return_words_as_error}}},
-        {"cause_log_fatal",             {0,     0,      {true,  do_cause_log_fatal}}},
-        {"generate_sane_error",         {0,     0,      {true,  do_generate_sane_error}}},
-        {"return_context_as_error",     {0,     0,      {true,  do_return_context_as_error}}},
-    };
-    // clang-format on
     return test_function_map;
 }
 
 }  // namespace init
 }  // namespace android
 
-// init_test.cpp contains the main entry point for all init tests.
-int SubcontextTestChildMain(int argc, char** argv) {
-    auto test_function_map = android::init::BuildTestFunctionMap();
-    return android::init::SubcontextMain(argc, argv, &test_function_map);
+int main(int argc, char** argv) {
+    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+        auto test_function_map = android::init::BuildTestFunctionMap();
+        return android::init::SubcontextMain(argc, argv, &test_function_map);
+    }
+
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
 }
diff --git a/init/test_function_map.h b/init/test_function_map.h
new file mode 100644
index 0000000..583df1a
--- /dev/null
+++ b/init/test_function_map.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_TEST_FUNCTION_MAP_H
+#define _INIT_TEST_FUNCTION_MAP_H
+
+#include <string>
+#include <vector>
+
+#include "builtin_arguments.h"
+#include "keyword_map.h"
+
+namespace android {
+namespace init {
+
+class TestFunctionMap : public KeywordFunctionMap {
+  public:
+    // Helper for argument-less functions
+    using BuiltinFunctionNoArgs = std::function<void(void)>;
+    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+        Add(name, 0, 0, false, [function](const BuiltinArguments&) {
+            function();
+            return Success();
+        });
+    }
+
+    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+             bool run_in_subcontext, const BuiltinFunction function) {
+        builtin_functions_[name] =
+            make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
+    }
+
+  private:
+    Map builtin_functions_ = {};
+
+    const Map& map() const override { return builtin_functions_; }
+};
+
+}  // namespace init
+}  // namespace android
+
+#endif
diff --git a/init/test_utils/Android.bp b/init/test_utils/Android.bp
deleted file mode 100644
index 1cb05b6..0000000
--- a/init/test_utils/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-cc_library_static {
-    name: "libinit_test_utils",
-    cflags: [
-        "-Wall",
-        "-Wextra",
-        "-Wno-unused-parameter",
-        "-Werror",
-    ],
-    srcs: [
-        "service_utils.cpp",
-    ],
-    shared_libs: [
-        "libcutils",
-        "liblog",
-        "libjsoncpp",
-        "libprotobuf-cpp-lite",
-        "libhidl-gen-utils",
-    ],
-    whole_static_libs: [
-        "libinit",
-        "libpropertyinfoparser",
-    ],
-    static_libs: [
-        "libbase",
-    ],
-    export_include_dirs: ["include"], // for tests
-}
diff --git a/init/test_utils/include/init-test-utils/service_utils.h b/init/test_utils/include/init-test-utils/service_utils.h
deleted file mode 100644
index 3ec61d4..0000000
--- a/init/test_utils/include/init-test-utils/service_utils.h
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// 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 <map>
-#include <set>
-
-#include <android-base/result.h>
-#include <hidl-util/FqInstance.h>
-
-namespace android {
-namespace init {
-
-using ServiceInterfacesMap = std::map<std::string, std::set<android::FqInstance>>;
-android::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap();
-
-}  // namespace init
-}  // namespace android
diff --git a/init/test_utils/service_utils.cpp b/init/test_utils/service_utils.cpp
deleted file mode 100644
index bc00702..0000000
--- a/init/test_utils/service_utils.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// 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 <string>
-
-#include <android-base/logging.h>
-
-#include "../parser.h"
-#include "../service.h"
-#include "../service_list.h"
-#include "../service_parser.h"
-#include "include/init-test-utils/service_utils.h"
-
-namespace android {
-namespace init {
-
-android::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap() {
-    ServiceList& service_list = ServiceList::GetInstance();
-    Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
-    for (const auto& location : {
-                 "/init.rc",
-                 "/system/etc/init",
-                 "/system_ext/etc/init",
-                 "/product/etc/init",
-                 "/odm/etc/init",
-                 "/vendor/etc/init",
-         }) {
-        parser.ParseConfig(location);
-    }
-
-    ServiceInterfacesMap result;
-    for (const auto& service : service_list.services()) {
-        // Create an entry for all services, including services that may not
-        // have any declared interfaces.
-        result[service->name()] = std::set<android::FqInstance>();
-        for (const auto& intf : service->interfaces()) {
-            android::FqInstance fqInstance;
-            if (!fqInstance.setTo(intf)) {
-                return android::base::Error() << "Unable to parse interface: '" << intf << "'";
-            }
-            result[service->name()].insert(fqInstance);
-        }
-    }
-    return result;
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
index 6b31683..acfc7c7 100644
--- a/init/tokenizer_test.cpp
+++ b/init/tokenizer_test.cpp
@@ -46,7 +46,6 @@
                 return;
             case T_NEWLINE:
                 tokens.emplace_back(std::move(current_line));
-                current_line.clear();
                 break;
             case T_TEXT:
                 current_line.emplace_back(state.text);
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 416d942..62cd2be 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -131,7 +131,7 @@
                                                        const ListenerCallback& callback) const {
     int dfd = dirfd(d);
 
-    int fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
+    int fd = openat(dfd, "uevent", O_WRONLY);
     if (fd >= 0) {
         write(fd, "add\n", 4);
         close(fd);
@@ -146,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 | O_CLOEXEC);
+        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
         if (fd < 0) continue;
 
         std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
@@ -171,7 +171,7 @@
     return RegenerateUeventsForDir(d.get(), callback);
 }
 
-static const char* kRegenerationPaths[] = {"/sys/devices"};
+static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
 
 void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
     for (const auto path : kRegenerationPaths) {
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 59f91ee..399ea4c 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -17,15 +17,12 @@
 #include "ueventd.h"
 
 #include <ctype.h>
-#include <dirent.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/stat.h>
 #include <sys/wait.h>
-#include <unistd.h>
 
 #include <set>
 #include <thread>
@@ -40,7 +37,6 @@
 #include "devices.h"
 #include "firmware_handler.h"
 #include "modalias_handler.h"
-#include "selabel.h"
 #include "selinux.h"
 #include "uevent_handler.h"
 #include "uevent_listener.h"
@@ -113,12 +109,10 @@
 class ColdBoot {
   public:
     ColdBoot(UeventListener& uevent_listener,
-             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers,
-             bool enable_parallel_restorecon)
+             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers)
         : uevent_listener_(uevent_listener),
           uevent_handlers_(uevent_handlers),
-          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4),
-          enable_parallel_restorecon_(enable_parallel_restorecon) {}
+          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
 
     void Run();
 
@@ -126,21 +120,16 @@
     void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);
     void RegenerateUevents();
     void ForkSubProcesses();
+    void DoRestoreCon();
     void WaitForSubProcesses();
-    void RestoreConHandler(unsigned int process_num, unsigned int total_processes);
-    void GenerateRestoreCon(const std::string& directory);
 
     UeventListener& uevent_listener_;
     std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;
 
     unsigned int num_handler_subprocesses_;
-    bool enable_parallel_restorecon_;
-
     std::vector<Uevent> uevent_queue_;
 
     std::set<pid_t> subprocess_pids_;
-
-    std::vector<std::string> restorecon_queue_;
 };
 
 void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
@@ -151,40 +140,12 @@
             uevent_handler->HandleUevent(uevent);
         }
     }
-}
-
-void ColdBoot::RestoreConHandler(unsigned int process_num, unsigned int total_processes) {
-    for (unsigned int i = process_num; i < restorecon_queue_.size(); i += total_processes) {
-        auto& dir = restorecon_queue_[i];
-
-        selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-    }
-}
-
-void ColdBoot::GenerateRestoreCon(const std::string& directory) {
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);
-
-    if (!dir) return;
-
-    struct dirent* dent;
-    while ((dent = readdir(dir.get())) != NULL) {
-        if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue;
-
-        struct stat st;
-        if (fstatat(dirfd(dir.get()), dent->d_name, &st, 0) == -1) continue;
-
-        if (S_ISDIR(st.st_mode)) {
-            std::string fullpath = directory + "/" + dent->d_name;
-            if (fullpath != "/sys/devices") {
-                restorecon_queue_.emplace_back(fullpath);
-            }
-        }
-    }
+    _exit(EXIT_SUCCESS);
 }
 
 void ColdBoot::RegenerateUevents() {
     uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
-        uevent_queue_.emplace_back(uevent);
+        uevent_queue_.emplace_back(std::move(uevent));
         return ListenerAction::kContinue;
     });
 }
@@ -198,16 +159,16 @@
 
         if (pid == 0) {
             UeventHandlerMain(i, num_handler_subprocesses_);
-            if (enable_parallel_restorecon_) {
-                RestoreConHandler(i, num_handler_subprocesses_);
-            }
-            _exit(EXIT_SUCCESS);
         }
 
         subprocess_pids_.emplace(pid);
     }
 }
 
+void ColdBoot::DoRestoreCon() {
+    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+}
+
 void ColdBoot::WaitForSubProcesses() {
     // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets
     // stuck.
@@ -246,23 +207,13 @@
 
     RegenerateUevents();
 
-    if (enable_parallel_restorecon_) {
-        selinux_android_restorecon("/sys", 0);
-        selinux_android_restorecon("/sys/devices", 0);
-        GenerateRestoreCon("/sys");
-        // takes long time for /sys/devices, parallelize it
-        GenerateRestoreCon("/sys/devices");
-    }
-
     ForkSubProcesses();
 
-    if (!enable_parallel_restorecon_) {
-        selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
-    }
+    DoRestoreCon();
 
     WaitForSubProcesses();
 
-    android::base::SetProperty(kColdBootDoneProp, "true");
+    close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
     LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
 }
 
@@ -296,18 +247,15 @@
             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),
-            std::move(ueventd_configuration.external_firmware_handlers)));
+            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));
+        uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>());
     }
     UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
 
-    if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
-        ColdBoot cold_boot(uevent_listener, uevent_handlers,
-                           ueventd_configuration.enable_parallel_restorecon);
+    if (access(COLDBOOT_DONE, F_OK) != 0) {
+        ColdBoot cold_boot(uevent_listener, uevent_handlers);
         cold_boot.Run();
     }
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index a74b247..aac3fe5 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -29,9 +29,9 @@
 namespace android {
 namespace init {
 
-Result<void> ParsePermissionsLine(std::vector<std::string>&& args,
-                                  std::vector<SysfsPermissions>* out_sysfs_permissions,
-                                  std::vector<Permissions>* out_dev_permissions) {
+Result<Success> ParsePermissionsLine(std::vector<std::string>&& args,
+                                     std::vector<SysfsPermissions>* out_sysfs_permissions,
+                                     std::vector<Permissions>* out_dev_permissions) {
     bool is_sysfs = out_sysfs_permissions != nullptr;
     if (is_sysfs && args.size() != 5) {
         return Error() << "/sys/ lines must have 5 entries";
@@ -74,63 +74,39 @@
     } else {
         out_dev_permissions->emplace_back(name, perm, uid, gid);
     }
-    return {};
+    return Success();
 }
 
-Result<void> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
-                                          std::vector<std::string>* firmware_directories) {
+Result<Success> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
+                                             std::vector<std::string>* firmware_directories) {
     if (args.size() < 2) {
         return Error() << "firmware_directories must have at least 1 entry";
     }
 
     std::move(std::next(args.begin()), args.end(), std::back_inserter(*firmware_directories));
 
-    return {};
+    return Success();
 }
 
-Result<void> ParseExternalFirmwareHandlerLine(
-        std::vector<std::string>&& args,
-        std::vector<ExternalFirmwareHandler>* external_firmware_handlers) {
-    if (args.size() != 4) {
-        return Error() << "external_firmware_handler lines must have exactly 3 parameters";
-    }
-
-    if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
-                     [&args](const auto& other) { return other.devpath == args[2]; }) !=
-        external_firmware_handlers->end()) {
-        return Error() << "found a previous external_firmware_handler with the same devpath, '"
-                       << args[2] << "'";
-    }
-
-    passwd* pwd = getpwnam(args[2].c_str());
-    if (!pwd) {
-        return ErrnoError() << "invalid handler uid'" << args[2] << "'";
-    }
-
-    ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, std::move(args[3]));
-    external_firmware_handlers->emplace_back(std::move(handler));
-
-    return {};
-}
-
-Result<void> ParseEnabledDisabledLine(std::vector<std::string>&& args, bool* feature) {
+Result<Success> ParseModaliasHandlingLine(std::vector<std::string>&& args,
+                                          bool* enable_modalias_handling) {
     if (args.size() != 2) {
-        return Error() << args[0] << " lines take exactly one parameter";
+        return Error() << "modalias_handling lines take exactly one parameter";
     }
 
     if (args[1] == "enabled") {
-        *feature = true;
+        *enable_modalias_handling = true;
     } else if (args[1] == "disabled") {
-        *feature = false;
+        *enable_modalias_handling = false;
     } else {
-        return Error() << args[0] << " takes either 'enabled' or 'disabled' as a parameter";
+        return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
     }
 
-    return {};
+    return Success();
 }
 
-Result<void> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
-                                             size_t* uevent_socket_rcvbuf_size) {
+Result<Success> 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";
     }
@@ -142,27 +118,27 @@
 
     *uevent_socket_rcvbuf_size = parsed_size;
 
-    return {};
+    return Success();
 }
 
 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;
+    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<void> ParseDevName(std::vector<std::string>&& args);
-    Result<void> ParseDirName(std::vector<std::string>&& args);
+    Result<Success> ParseDevName(std::vector<std::string>&& args);
+    Result<Success> 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) {
+Result<Success> 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";
     }
@@ -173,51 +149,58 @@
 
     subsystem_ = Subsystem(std::move(args[1]));
 
-    return {};
+    return Success();
 }
 
-Result<void> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
+Result<Success> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {
     if (args[1] == "uevent_devname") {
         subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVNAME;
-        return {};
+        return Success();
     }
     if (args[1] == "uevent_devpath") {
         subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;
-        return {};
+        return Success();
     }
 
     return Error() << "invalid devname '" << args[1] << "'";
 }
 
-Result<void> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {
+Result<Success> 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 {};
+    return Success();
 }
 
-Result<void> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
-    using OptionParser = Result<void> (SubsystemParser::*)(std::vector<std::string> && args);
-    // clang-format off
-    static const KeywordMap<OptionParser> parser_map = {
-        {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
-        {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
-    };
-    // clang-format on
+Result<Success> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
+    using OptionParser = Result<Success> (SubsystemParser::*)(std::vector<std::string> && args);
 
-    auto parser = parser_map.Find(args);
+    static class OptionParserMap : public KeywordMap<OptionParser> {
+      private:
+        const Map& map() const override {
+            // clang-format off
+            static const Map option_parsers = {
+                {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
+                {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
+            };
+            // clang-format on
+            return option_parsers;
+        }
+    } parser_map;
+
+    auto parser = parser_map.FindFunction(args);
 
     if (!parser) return Error() << parser.error();
 
     return std::invoke(*parser, this, std::move(args));
 }
 
-Result<void> SubsystemParser::EndSection() {
+Result<Success> SubsystemParser::EndSection() {
     subsystems_->emplace_back(std::move(subsystem_));
 
-    return {};
+    return Success();
 }
 
 UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
@@ -236,18 +219,12 @@
     parser.AddSingleLineParser("firmware_directories",
                                std::bind(ParseFirmwareDirectoriesLine, _1,
                                          &ueventd_configuration.firmware_directories));
-    parser.AddSingleLineParser("external_firmware_handler",
-                               std::bind(ParseExternalFirmwareHandlerLine, _1,
-                                         &ueventd_configuration.external_firmware_handlers));
     parser.AddSingleLineParser("modalias_handling",
-                               std::bind(ParseEnabledDisabledLine, _1,
+                               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));
-    parser.AddSingleLineParser("parallel_restorecon",
-                               std::bind(ParseEnabledDisabledLine, _1,
-                                         &ueventd_configuration.enable_parallel_restorecon));
 
     for (const auto& config : configs) {
         parser.ParseConfig(config);
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index eaafa5a..d476dec 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_UEVENTD_PARSER_H
+#define _INIT_UEVENTD_PARSER_H
 
 #include <string>
 #include <vector>
 
 #include "devices.h"
-#include "firmware_handler.h"
 
 namespace android {
 namespace init {
@@ -30,13 +30,13 @@
     std::vector<SysfsPermissions> sysfs_permissions;
     std::vector<Permissions> dev_permissions;
     std::vector<std::string> firmware_directories;
-    std::vector<ExternalFirmwareHandler> external_firmware_handlers;
     bool enable_modalias_handling = false;
     size_t uevent_socket_rcvbuf_size = 0;
-    bool enable_parallel_restorecon = false;
 };
 
 UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
 
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index 172ba0b..9c1cedf 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -20,8 +20,6 @@
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 
-#include "firmware_handler.h"
-
 namespace android {
 namespace init {
 
@@ -95,7 +93,7 @@
             {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
             {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
 
-    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}});
+    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}});
 }
 
 TEST(ueventd_parser, Permissions) {
@@ -121,7 +119,7 @@
             {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
     };
 
-    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}});
+    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}});
 }
 
 TEST(ueventd_parser, FirmwareDirectories) {
@@ -137,52 +135,7 @@
             "/more",
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}});
-}
-
-TEST(ueventd_parser, ExternalFirmwareHandlers) {
-    auto ueventd_file = R"(
-external_firmware_handler devpath root handler_path
-external_firmware_handler /devices/path/firmware/something001.bin system /vendor/bin/firmware_handler.sh
-external_firmware_handler /devices/path/firmware/something001.bin radio "/vendor/bin/firmware_handler.sh --has --arguments"
-)";
-
-    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
-            {
-                    "devpath",
-                    AID_ROOT,
-                    "handler_path",
-            },
-            {
-                    "/devices/path/firmware/something001.bin",
-                    AID_SYSTEM,
-                    "/vendor/bin/firmware_handler.sh",
-            },
-            {
-                    "/devices/path/firmware/something001.bin",
-                    AID_RADIO,
-                    "/vendor/bin/firmware_handler.sh --has --arguments",
-            },
-    };
-
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
-}
-
-TEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) {
-    auto ueventd_file = R"(
-external_firmware_handler devpath root handler_path
-external_firmware_handler devpath root handler_path2
-)";
-
-    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
-            {
-                    "devpath",
-                    AID_ROOT,
-                    "handler_path",
-            },
-    };
-
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories});
 }
 
 TEST(ueventd_parser, UeventSocketRcvbufSize) {
@@ -191,25 +144,7 @@
 uevent_socket_rcvbuf_size 8M
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
-}
-
-TEST(ueventd_parser, EnabledDisabledLines) {
-    auto ueventd_file = R"(
-modalias_handling enabled
-parallel_restorecon enabled
-modalias_handling disabled
-)";
-
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 0, true});
-
-    auto ueventd_file2 = R"(
-parallel_restorecon enabled
-modalias_handling enabled
-parallel_restorecon disabled
-)";
-
-    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, true, 0, false});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, false, 8 * 1024 * 1024});
 }
 
 TEST(ueventd_parser, AllTogether) {
@@ -243,11 +178,7 @@
 /sys/devices/virtual/*/input   poll_delay  0660  root   input
 firmware_directories /more
 
-external_firmware_handler /devices/path/firmware/firmware001.bin root /vendor/bin/touch.sh
-
 uevent_socket_rcvbuf_size 6M
-modalias_handling enabled
-parallel_restorecon enabled
 
 #ending comment
 )";
@@ -277,15 +208,10 @@
             "/more",
     };
 
-    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
-            {"/devices/path/firmware/firmware001.bin", AID_ROOT, "/vendor/bin/touch.sh"},
-    };
-
     size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
 
-    TestUeventdFile(ueventd_file,
-                    {subsystems, sysfs_permissions, permissions, firmware_directories,
-                     external_firmware_handlers, true, uevent_socket_rcvbuf_size, true});
+    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.
@@ -304,18 +230,6 @@
 
 subsystem #no name
 
-modalias_handling
-modalias_handling enabled enabled
-modalias_handling blah
-
-parallel_restorecon
-parallel_restorecon enabled enabled
-parallel_restorecon blah
-
-external_firmware_handler
-external_firmware_handler blah blah
-external_firmware_handler blah blah blah blah
-
 )";
 
     TestUeventdFile(ueventd_file, {});
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index 2d7d2f8..bfdc28e 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -68,7 +68,7 @@
 
 TEST(ueventd, setegid_IsPerThread) {
     if (getuid() != 0) {
-        GTEST_SKIP() << "Skipping test, must be run as root.";
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
         return;
     }
 
@@ -92,11 +92,11 @@
 
 TEST(ueventd, setfscreatecon_IsPerThread) {
     if (getuid() != 0) {
-        GTEST_SKIP() << "Skipping test, must be run as root.";
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
         return;
     }
     if (!is_selinux_enabled() || security_getenforce() == 1) {
-        GTEST_SKIP() << "Skipping test, SELinux must be enabled and in permissive mode.";
+        GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
         return;
     }
 
@@ -127,7 +127,7 @@
 
 TEST(ueventd, selabel_lookup_MultiThreaded) {
     if (getuid() != 0) {
-        GTEST_SKIP() << "Skipping test, must be run as root.";
+        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
         return;
     }
 
diff --git a/init/util.cpp b/init/util.cpp
index ada9e78..63d2d44 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -20,7 +20,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <pwd.h>
-#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -35,25 +34,23 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include <selinux/android.h>
 
 #if defined(__ANDROID__)
-#include <android/api-level.h>
-#include <sys/system_properties.h>
-
 #include "reboot_utils.h"
-#include "selabel.h"
 #include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
 
+#ifdef _INIT_INIT_H
+#error "Do not include init.h in files used by ueventd; it will expose init's globals"
+#endif
+
 using android::base::boot_clock;
-using android::base::StartsWith;
 using namespace std::literals::string_literals;
 
 namespace android {
@@ -84,28 +81,32 @@
  * daemon. We communicate the file descriptor's value via the environment
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
-Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
-                         gid_t gid, const std::string& socketcon) {
-    if (!socketcon.empty()) {
-        if (setsockcreatecon(socketcon.c_str()) == -1) {
-            return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";
+int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
+                 const char* socketcon) {
+    if (socketcon) {
+        if (setsockcreatecon(socketcon) == -1) {
+            PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
+            return -1;
         }
     }
 
     android::base::unique_fd fd(socket(PF_UNIX, type, 0));
     if (fd < 0) {
-        return ErrnoError() << "Failed to open socket '" << name << "'";
+        PLOG(ERROR) << "Failed to open socket '" << name << "'";
+        return -1;
     }
 
-    if (!socketcon.empty()) setsockcreatecon(nullptr);
+    if (socketcon) setsockcreatecon(NULL);
 
     struct sockaddr_un addr;
     memset(&addr, 0 , sizeof(addr));
     addr.sun_family = AF_UNIX;
-    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR "/%s", name.c_str());
+    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
+             name);
 
     if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {
-        return ErrnoError() << "Failed to unlink old socket '" << name << "'";
+        PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
+        return -1;
     }
 
     std::string secontext;
@@ -116,7 +117,8 @@
     if (passcred) {
         int on = 1;
         if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
-            return ErrnoError() << "Failed to set SO_PASSCRED '" << name << "'";
+            PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
+            return -1;
         }
     }
 
@@ -127,18 +129,19 @@
         setfscreatecon(nullptr);
     }
 
-    auto guard = android::base::make_scope_guard([&addr] { unlink(addr.sun_path); });
-
     if (ret) {
         errno = savederrno;
-        return ErrnoError() << "Failed to bind socket '" << name << "'";
+        PLOG(ERROR) << "Failed to bind socket '" << name << "'";
+        goto out_unlink;
     }
 
     if (lchown(addr.sun_path, uid, gid)) {
-        return ErrnoError() << "Failed to lchown socket '" << addr.sun_path << "'";
+        PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
+        goto out_unlink;
     }
     if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
-        return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";
+        PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
+        goto out_unlink;
     }
 
     LOG(INFO) << "Created socket '" << addr.sun_path << "'"
@@ -146,8 +149,11 @@
               << ", user " << uid
               << ", group " << gid;
 
-    guard.Disable();
     return fd.release();
+
+out_unlink:
+    unlink(addr.sun_path);
+    return -1;
 }
 
 Result<std::string> ReadFile(const std::string& path) {
@@ -191,7 +197,7 @@
     return rc;
 }
 
-Result<void> WriteFile(const std::string& path, const std::string& content) {
+Result<Success> 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) {
@@ -200,7 +206,7 @@
     if (!android::base::WriteStringToFd(content, fd)) {
         return ErrnoError() << "Unable to write file contents";
     }
-    return {};
+    return Success();
 }
 
 bool mkdir_recursive(const std::string& path, mode_t mode) {
@@ -273,10 +279,12 @@
     return S_ISDIR(info.st_mode);
 }
 
-Result<std::string> ExpandProps(const std::string& src) {
+bool expand_props(const std::string& src, std::string* dst) {
     const char* src_ptr = src.c_str();
 
-    std::string dst;
+    if (!dst) {
+        return false;
+    }
 
     /* - variables can either be $x.y or ${x.y}, in case they are only part
      *   of the string.
@@ -290,19 +298,19 @@
 
         c = strchr(src_ptr, '$');
         if (!c) {
-            dst.append(src_ptr);
-            return dst;
+            dst->append(src_ptr);
+            return true;
         }
 
-        dst.append(src_ptr, c);
+        dst->append(src_ptr, c);
         c++;
 
         if (*c == '$') {
-            dst.push_back(*(c++));
+            dst->push_back(*(c++));
             src_ptr = c;
             continue;
         } else if (*c == '\0') {
-            return dst;
+            return true;
         }
 
         std::string prop_name;
@@ -312,7 +320,8 @@
             const char* end = strchr(c, '}');
             if (!end) {
                 // failed to find closing brace, abort.
-                return Error() << "unexpected end of string in '" << src << "', looking for }";
+                LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
+                return false;
             }
             prop_name = std::string(c, end);
             c = end + 1;
@@ -323,34 +332,29 @@
             }
         } else {
             prop_name = c;
-            if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
-                return Error() << "using deprecated syntax for specifying property '" << c
-                               << "', use ${name} instead";
-            } else {
-                LOG(ERROR) << "using deprecated syntax for specifying property '" << c
-                           << "', use ${name} instead";
-            }
+            LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
             c += prop_name.size();
         }
 
         if (prop_name.empty()) {
-            return Error() << "invalid zero-length property name in '" << src << "'";
+            LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
+            return false;
         }
 
         std::string prop_val = android::base::GetProperty(prop_name, "");
         if (prop_val.empty()) {
             if (def_val.empty()) {
-                return Error() << "property '" << prop_name << "' doesn't exist while expanding '"
-                               << src << "'";
+                LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
+                return false;
             }
             prop_val = def_val;
         }
 
-        dst.append(prop_val);
+        dst->append(prop_val);
         src_ptr = c;
     }
 
-    return dst;
+    return true;
 }
 
 static std::string init_android_dt_dir() {
@@ -422,194 +426,6 @@
     return true;
 }
 
-Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value) {
-    if (value.size() >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
-        return Error() << "Property value too long";
-    }
-
-    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
-        return Error() << "Value is not a UTF8 encoded string";
-    }
-
-    return {};
-}
-
-static FscryptAction FscryptInferAction(const std::string& dir) {
-    const std::string prefix = "/data/";
-
-    if (!android::base::StartsWith(dir, prefix)) {
-        return FscryptAction::kNone;
-    }
-
-    // Special-case /data/media/obb per b/64566063
-    if (dir == "/data/media/obb") {
-        // Try to set policy on this directory, but if it is non-empty this may fail.
-        return FscryptAction::kAttempt;
-    }
-
-    // Only set policy on first level /data directories
-    // To make this less restrictive, consider using a policy file.
-    // However this is overkill for as long as the policy is simply
-    // to apply a global policy to all /data folders created via makedir
-    if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
-        return FscryptAction::kNone;
-    }
-
-    // Special case various directories that must not be encrypted,
-    // often because their subdirectories must be encrypted.
-    // This isn't a nice way to do this, see b/26641735
-    std::vector<std::string> directories_to_exclude = {
-            "lost+found", "system_ce", "system_de", "misc_ce",     "misc_de",
-            "vendor_ce",  "vendor_de", "media",     "data",        "user",
-            "user_de",    "apex",      "preloads",  "app-staging", "gsi",
-    };
-    for (const auto& d : directories_to_exclude) {
-        if ((prefix + d) == dir) {
-            return FscryptAction::kNone;
-        }
-    }
-    // Empty these directories if policy setting fails.
-    std::vector<std::string> wipe_on_failure = {
-            "rollback", "rollback-observer",  // b/139193659
-    };
-    for (const auto& d : wipe_on_failure) {
-        if ((prefix + d) == dir) {
-            return FscryptAction::kDeleteIfNecessary;
-        }
-    }
-    return FscryptAction::kRequire;
-}
-
-Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {
-    mode_t mode = 0755;
-    Result<uid_t> uid = -1;
-    Result<gid_t> gid = -1;
-    FscryptAction fscrypt_inferred_action = FscryptInferAction(args[1]);
-    FscryptAction fscrypt_action = fscrypt_inferred_action;
-    std::string ref_option = "ref";
-    bool set_option_encryption = false;
-    bool set_option_key = false;
-
-    for (size_t i = 2; i < args.size(); i++) {
-        switch (i) {
-            case 2:
-                mode = std::strtoul(args[2].c_str(), 0, 8);
-                break;
-            case 3:
-                uid = DecodeUid(args[3]);
-                if (!uid) {
-                    return Error()
-                           << "Unable to decode UID for '" << args[3] << "': " << uid.error();
-                }
-                break;
-            case 4:
-                gid = DecodeUid(args[4]);
-                if (!gid) {
-                    return Error()
-                           << "Unable to decode GID for '" << args[4] << "': " << gid.error();
-                }
-                break;
-            default:
-                auto parts = android::base::Split(args[i], "=");
-                if (parts.size() != 2) {
-                    return Error() << "Can't parse option: '" << args[i] << "'";
-                }
-                auto optname = parts[0];
-                auto optval = parts[1];
-                if (optname == "encryption") {
-                    if (set_option_encryption) {
-                        return Error() << "Duplicated option: '" << optname << "'";
-                    }
-                    if (optval == "Require") {
-                        fscrypt_action = FscryptAction::kRequire;
-                    } else if (optval == "None") {
-                        fscrypt_action = FscryptAction::kNone;
-                    } else if (optval == "Attempt") {
-                        fscrypt_action = FscryptAction::kAttempt;
-                    } else if (optval == "DeleteIfNecessary") {
-                        fscrypt_action = FscryptAction::kDeleteIfNecessary;
-                    } else {
-                        return Error() << "Unknown encryption option: '" << optval << "'";
-                    }
-                    set_option_encryption = true;
-                } else if (optname == "key") {
-                    if (set_option_key) {
-                        return Error() << "Duplicated option: '" << optname << "'";
-                    }
-                    if (optval == "ref" || optval == "per_boot_ref") {
-                        ref_option = optval;
-                    } else {
-                        return Error() << "Unknown key option: '" << optval << "'";
-                    }
-                    set_option_key = true;
-                } else {
-                    return Error() << "Unknown option: '" << args[i] << "'";
-                }
-        }
-    }
-    if (set_option_key && fscrypt_action == FscryptAction::kNone) {
-        return Error() << "Key option set but encryption action is none";
-    }
-    const std::string prefix = "/data/";
-    if (StartsWith(args[1], prefix) &&
-        args[1].find_first_of('/', prefix.size()) == std::string::npos) {
-        if (!set_option_encryption) {
-            LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << args[1]
-                         << " <mode> <uid> <gid> encryption=Require";
-        }
-        if (fscrypt_action == FscryptAction::kNone) {
-            LOG(INFO) << "Not setting encryption policy on: " << args[1];
-        }
-    }
-    if (fscrypt_action != fscrypt_inferred_action) {
-        LOG(WARNING) << "Inferred action different from explicit one, expected "
-                     << static_cast<int>(fscrypt_inferred_action) << " but got "
-                     << static_cast<int>(fscrypt_action);
-    }
-
-    return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
-}
-
-Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
-        const std::vector<std::string>& args) {
-    struct flag_type {
-        const char* name;
-        int value;
-    };
-    static const flag_type flags[] = {
-            {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
-            {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
-            {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
-            {0, 0}};
-
-    int flag = 0;
-    std::vector<std::string> paths;
-
-    bool in_flags = true;
-    for (size_t i = 1; i < args.size(); ++i) {
-        if (android::base::StartsWith(args[i], "--")) {
-            if (!in_flags) {
-                return Error() << "flags must precede paths";
-            }
-            bool found = false;
-            for (size_t j = 0; flags[j].name; ++j) {
-                if (args[i] == flags[j].name) {
-                    flag |= flags[j].value;
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) {
-                return Error() << "bad flag " << args[i];
-            }
-        } else {
-            in_flags = false;
-            paths.emplace_back(args[i]);
-        }
-    }
-    return std::pair(flag, paths);
-}
-
 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.
@@ -618,7 +434,7 @@
         return;
     }
 
-    InitFatalReboot(SIGABRT);
+    InitFatalReboot();
 }
 
 // The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial
@@ -638,7 +454,7 @@
 // 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)
+    int fd = open("/dev/null", O_RDWR);
     if (fd == -1) {
         int saved_errno = errno;
         android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
diff --git a/init/util.h b/init/util.h
index 3d81d72..767620b 100644
--- a/init/util.h
+++ b/init/util.h
@@ -14,32 +14,35 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _INIT_UTIL_H_
+#define _INIT_UTIL_H_
 
 #include <sys/stat.h>
 #include <sys/types.h>
 
 #include <chrono>
 #include <functional>
+#include <ostream>
 #include <string>
 
 #include <android-base/chrono_utils.h>
+#include <selinux/label.h>
 
-#include "fscrypt_init_extensions.h"
 #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";
-
-Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
-                         gid_t gid, const std::string& socketcon);
+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<void> WriteFile(const std::string& path, const std::string& content);
+Result<Success> WriteFile(const std::string& path, const std::string& content);
 
 Result<uid_t> DecodeUid(const std::string& name);
 
@@ -49,7 +52,7 @@
                            const std::function<void(const std::string&, const std::string&, bool)>&);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
-Result<std::string> ExpandProps(const std::string& src);
+bool expand_props(const std::string& src, std::string* dst);
 
 // Returns the platform's Android DT directory as specified in the kernel cmdline.
 // If the platform does not configure a custom DT path, returns the standard one (based in procfs).
@@ -59,24 +62,11 @@
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
 
 bool IsLegalPropertyName(const std::string& name);
-Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
-
-struct MkdirOptions {
-    std::string target;
-    mode_t mode;
-    uid_t uid;
-    gid_t gid;
-    FscryptAction fscrypt_action;
-    std::string ref_option;
-};
-
-Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args);
-
-Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
-        const std::vector<std::string>& args);
 
 void SetStdioToDevNull(char** argv);
 void InitKernelLogging(char** argv);
 bool IsRecoveryMode();
 }  // namespace init
 }  // namespace android
+
+#endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index a8fcc87..1b5afba 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -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().message());
+    EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
 }
 
 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().message());
+    EXPECT_EQ("Skipping insecure file", file_contents.error_string());
 }
 
 TEST(util, ReadFileWorldWiteable) {
@@ -56,17 +56,16 @@
     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().message());
+    EXPECT_EQ("Skipping insecure file", file_contents.error_string());
 }
 
 TEST(util, ReadFileSymbolicLink) {
     errno = 0;
-    // lrw------- 1 root root 23 2008-12-31 19:00 default.prop -> system/etc/prop.default
-    auto file_contents = ReadFile("/default.prop");
+    // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
+    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().message());
+    EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
 }
 
 TEST(util, ReadFileSuccess) {
@@ -131,7 +130,7 @@
 
     decoded_uid = DecodeUid("toot");
     EXPECT_FALSE(decoded_uid);
-    EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error().message());
+    EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
 
     decoded_uid = DecodeUid("123");
     EXPECT_TRUE(decoded_uid);
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 565f2c3..7f9a18a 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -42,7 +42,6 @@
     name: "libbacktrace_headers",
     vendor_available: true,
     recovery_available: true,
-    native_bridge_supported: true,
     export_include_dirs: ["include"],
 }
 
@@ -97,6 +96,7 @@
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
         },
     },
+    whole_static_libs: ["libdemangle"],
 }
 
 cc_test_library {
@@ -134,6 +134,7 @@
     defaults: ["libbacktrace_common"],
     host_supported: true,
     srcs: [
+        "backtrace_offline_test.cpp",
         "backtrace_test.cpp",
     ],
 
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 3e050ab..71980d7 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -28,13 +28,13 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
+#include <demangle.h>
+
 #include "BacktraceLog.h"
 #include "UnwindStack.h"
 
 using android::base::StringPrintf;
 
-extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
-
 //-------------------------------------------------------------------------
 // Backtrace functions.
 //-------------------------------------------------------------------------
@@ -63,14 +63,7 @@
   if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
     return "";
   }
-  std::string name(GetFunctionNameRaw(pc, offset));
-  char* demangled_name = __cxa_demangle(name.c_str(), nullptr, nullptr, nullptr);
-  if (demangled_name != nullptr) {
-    name = demangled_name;
-    free(demangled_name);
-    return name;
-  }
-  return name;
+  return demangle(GetFunctionNameRaw(pc, offset).c_str());
 }
 
 bool Backtrace::VerifyReadWordArgs(uint64_t ptr, word_t* out_value) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 624711f..36640cd 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -24,6 +24,7 @@
 #include <string>
 
 #include <backtrace/Backtrace.h>
+#include <demangle.h>
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
@@ -40,8 +41,6 @@
 #include "UnwindStack.h"
 #include "UnwindStackMap.h"
 
-extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
-
 bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
                        std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
                        std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
@@ -116,13 +115,7 @@
     back_frame->pc = frame->pc;
     back_frame->sp = frame->sp;
 
-    char* demangled_name = __cxa_demangle(frame->function_name.c_str(), nullptr, nullptr, nullptr);
-    if (demangled_name != nullptr) {
-      back_frame->func_name = demangled_name;
-      free(demangled_name);
-    } else {
-      back_frame->func_name = frame->function_name;
-    }
+    back_frame->func_name = demangle(frame->function_name.c_str());
     back_frame->func_offset = frame->function_offset;
 
     back_frame->map.name = frame->map_name;
@@ -136,6 +129,22 @@
   return true;
 }
 
+bool Backtrace::UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
+                              const backtrace_stackinfo_t& stack,
+                              std::vector<backtrace_frame_data_t>* frames,
+                              BacktraceUnwindError* error) {
+  UnwindStackOfflineMap* offline_map = reinterpret_cast<UnwindStackOfflineMap*>(back_map);
+  // Create the process memory from the stack data since this will almost
+  // always be different each unwind.
+  if (!offline_map->CreateProcessMemory(stack)) {
+    if (error != nullptr) {
+      error->error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+    }
+    return false;
+  }
+  return Backtrace::Unwind(regs, back_map, frames, 0U, nullptr, error);
+}
+
 UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
     : BacktraceCurrent(pid, tid, map) {}
 
@@ -162,7 +171,7 @@
 }
 
 UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
-    : BacktracePtrace(pid, tid, map), memory_(unwindstack::Memory::CreateProcessMemory(pid)) {}
+    : BacktracePtrace(pid, tid, map), memory_(pid) {}
 
 std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
   return GetMap()->GetFunctionName(pc, offset);
@@ -180,5 +189,73 @@
 }
 
 size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
-  return memory_->Read(addr, buffer, bytes);
+  return memory_.Read(addr, buffer, bytes);
+}
+
+UnwindStackOffline::UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map,
+                                       bool map_shared)
+    : Backtrace(pid, tid, map), arch_(arch) {
+  map_shared_ = map_shared;
+}
+
+bool UnwindStackOffline::Unwind(size_t num_ignore_frames, void* ucontext) {
+  if (ucontext == nullptr) {
+    return false;
+  }
+
+  unwindstack::ArchEnum arch;
+  switch (arch_) {
+    case ARCH_ARM:
+      arch = unwindstack::ARCH_ARM;
+      break;
+    case ARCH_ARM64:
+      arch = unwindstack::ARCH_ARM64;
+      break;
+    case ARCH_X86:
+      arch = unwindstack::ARCH_X86;
+      break;
+    case ARCH_X86_64:
+      arch = unwindstack::ARCH_X86_64;
+      break;
+    default:
+      return false;
+  }
+
+  std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
+
+  return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
+}
+
+std::string UnwindStackOffline::GetFunctionNameRaw(uint64_t, uint64_t*) {
+  return "";
+}
+
+size_t UnwindStackOffline::Read(uint64_t, uint8_t*, size_t) {
+  return 0;
+}
+
+bool UnwindStackOffline::ReadWord(uint64_t, word_t*) {
+  return false;
+}
+
+Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
+                                    const std::vector<backtrace_map_t>& maps,
+                                    const backtrace_stackinfo_t& stack) {
+  std::unique_ptr<UnwindStackOfflineMap> map(
+      reinterpret_cast<UnwindStackOfflineMap*>(BacktraceMap::CreateOffline(pid, maps)));
+  if (map.get() == nullptr || !map->CreateProcessMemory(stack)) {
+    return nullptr;
+  }
+  return new UnwindStackOffline(arch, pid, tid, map.release(), false);
+}
+
+Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map) {
+  if (map == nullptr) {
+    return nullptr;
+  }
+  return new UnwindStackOffline(arch, pid, tid, map, true);
+}
+
+void Backtrace::SetGlobalElfCache(bool enable) {
+  unwindstack::Elf::SetCachingEnabled(enable);
 }
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
index 47f6757..4ec591d 100644
--- a/libbacktrace/UnwindStack.h
+++ b/libbacktrace/UnwindStack.h
@@ -19,7 +19,6 @@
 
 #include <stdint.h>
 
-#include <memory>
 #include <string>
 
 #include <backtrace/BacktraceMap.h>
@@ -50,7 +49,23 @@
   size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
 
  private:
-  std::shared_ptr<unwindstack::Memory> memory_;
+  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) override;
+
+  size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
+
+  bool ReadWord(uint64_t ptr, word_t* out_value) override;
+
+ private:
+  ArchEnum arch_;
 };
 
 #endif  // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index aa0b17c..4518891 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -132,6 +132,43 @@
   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.
 //-------------------------------------------------------------------------
@@ -152,3 +189,15 @@
   }
   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 f0e7d8b..e19b605 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -33,7 +33,6 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
 
 // Forward declarations.
 class UnwindDexFile;
@@ -75,4 +74,19 @@
   unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
 };
 
+class UnwindStackOfflineMap : public UnwindStackMap {
+ public:
+  UnwindStackOfflineMap(pid_t pid);
+  ~UnwindStackOfflineMap() = default;
+
+  bool Build() override;
+
+  bool Build(const std::vector<backtrace_map_t>& maps);
+
+  bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
+
+ private:
+  unwindstack::MemoryOfflineBuffer* memory_ = nullptr;
+};
+
 #endif  // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
new file mode 100644
index 0000000..662fb99
--- /dev/null
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -0,0 +1,397 @@
+/*
+ * 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 <android-base/threads.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#include <gtest/gtest.h>
+
+#include "BacktraceTest.h"
+
+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>(&BacktraceTest::test_level_one_), 0},
+      {"test_level_two", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_two_), 0},
+      {"test_level_three", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_three_), 0},
+      {"test_level_four", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_four_), 0},
+      {"test_recursive_call", reinterpret_cast<uint64_t>(&BacktraceTest::test_recursive_call_), 0},
+      {"test_get_context_and_wait",
+       reinterpret_cast<uint64_t>(&BacktraceTest::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 = android::base::GetThreadId();
+  BacktraceTest::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_F(BacktraceTest, 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_F(BacktraceTest, offline_eh_frame) {
+  BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
+  BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
+}
+
+TEST_F(BacktraceTest, offline_debug_frame) {
+  BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
+  BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
+}
+
+TEST_F(BacktraceTest, offline_gnu_debugdata) {
+  BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
+  BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
+}
+
+TEST_F(BacktraceTest, 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_F(BacktraceTest, offline_unwind_mix_eh_frame_and_arm_exidx) {
+  LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
+}
+
+TEST_F(BacktraceTest, offline_debug_frame_with_load_bias) {
+  LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
+}
+
+TEST_F(BacktraceTest, offline_try_armexidx_after_debug_frame) {
+  LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
+}
+
+TEST_F(BacktraceTest, 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_F(BacktraceTest, 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_F(BacktraceTest, 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/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 664b531..404e7e8 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -126,6 +126,24 @@
   // If map is not NULL, the map is still owned by the caller.
   static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
 
+  // Create an offline Backtrace object that can be used to do an unwind without a process
+  // that is still running. By default, information is only cached in the map
+  // file. If the calling code creates the map, data can be cached between
+  // unwinds. If not, all cached data will be destroyed when the Backtrace
+  // object is destroyed.
+  static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
+                                  const std::vector<backtrace_map_t>& maps,
+                                  const backtrace_stackinfo_t& stack);
+  static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map);
+
+  // Create an offline Backtrace object that can be used to do an unwind without a process
+  // that is still running. If cache_file is set to true, then elf information will be cached
+  // 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);
+
   virtual ~Backtrace();
 
   // Get the current stack trace and store in the backtrace_ structure.
@@ -135,6 +153,11 @@
                      std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
                      std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
 
+  static bool UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
+                            const backtrace_stackinfo_t& stack_info,
+                            std::vector<backtrace_frame_data_t>* frames,
+                            BacktraceUnwindError* error = nullptr);
+
   // Get the function name and offset into the function given the pc.
   // If the string is empty, then no valid function name was found,
   // or the pc is not in any valid map.
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index e000a00..c564271 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -69,6 +69,8 @@
   // 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*> {
@@ -79,7 +81,7 @@
       index_++;
       return *this;
     }
-    const iterator operator++(int increment) {
+    iterator& operator++(int increment) {
       index_ += increment;
       return *this;
     }
@@ -87,7 +89,7 @@
       index_--;
       return *this;
     }
-    const iterator operator--(int decrement) {
+    iterator& operator--(int decrement) {
       index_ -= decrement;
       return *this;
     }
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 88e1bdb..619bc56 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -34,7 +34,6 @@
     vendor_available: true,
     recovery_available: true,
     host_supported: true,
-    native_bridge_supported: true,
     export_include_dirs: ["include"],
     target: {
         vendor: {
@@ -58,7 +57,6 @@
     },
     recovery_available: true,
     host_supported: true,
-    native_bridge_supported: true,
     srcs: [
         "config_utils.cpp",
         "canned_fs_config.cpp",
@@ -67,6 +65,8 @@
         "native_handle.cpp",
         "record_stream.cpp",
         "sockets.cpp",
+        "strdup16to8.cpp",
+        "strdup8to16.cpp",
         "strlcpy.c",
         "threads.cpp",
     ],
@@ -244,7 +244,6 @@
     defaults: ["libcutils_test_default"],
     host_supported: true,
     shared_libs: test_libraries,
-    require_root: true,
 }
 
 cc_test {
@@ -256,7 +255,6 @@
         "libcgrouprc_format",
     ] + test_libraries,
     stl: "libc++_static",
-    require_root: true,
 
     target: {
         android: {
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 340572c..e67b458 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -23,6 +23,9 @@
  */
 #define LOG_TAG "ashmem"
 
+#ifndef __ANDROID_VNDK__
+#include <dlfcn.h>
+#endif
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/ashmem.h>
@@ -39,11 +42,11 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android-base/file.h>
 #include <android-base/properties.h>
-#include <android-base/strings.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
 
@@ -63,6 +66,32 @@
 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
@@ -186,31 +215,25 @@
     return memfd_supported;
 }
 
-static std::string get_ashmem_device_path() {
-    static const std::string boot_id_path = "/proc/sys/kernel/random/boot_id";
-    std::string boot_id;
-    if (!android::base::ReadFileToString(boot_id_path, &boot_id)) {
-        ALOGE("Failed to read %s: %s.\n", boot_id_path.c_str(), strerror(errno));
-        return "";
-    };
-    boot_id = android::base::Trim(boot_id);
-
-    return "/dev/ashmem" + boot_id;
-}
-
 /* logistics of getting file descriptor for ashmem */
 static int __ashmem_open_locked()
 {
-    static const std::string ashmem_device_path = get_ashmem_device_path();
-
     int ret;
     struct stat st;
 
-    if (ashmem_device_path.empty()) {
-        return -1;
+    int fd = -1;
+#ifndef __ANDROID_VNDK__
+    if (!openFd) {
+        openFd = initOpenAshmemFd();
     }
 
-    int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
+    if (openFd) {
+        fd = openFd();
+    }
+#endif
+    if (fd < 0) {
+        fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
+    }
     if (fd < 0) {
         return fd;
     }
@@ -462,3 +485,11 @@
 
     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 2ba1eb0..32446d4 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -34,29 +34,6 @@
 
 #include <utils/Compat.h>
 
-static bool ashmem_validate_stat(int fd, struct stat* buf) {
-    int result = fstat(fd, buf);
-    if (result == -1) {
-        return false;
-    }
-
-    /*
-     * Check if this is an "ashmem" region.
-     * TODO: This is very hacky, and can easily break.
-     * We need some reliable indicator.
-     */
-    if (!(buf->st_nlink == 0 && S_ISREG(buf->st_mode))) {
-        errno = ENOTTY;
-        return false;
-    }
-    return true;
-}
-
-int ashmem_valid(int fd) {
-    struct stat buf;
-    return ashmem_validate_stat(fd, &buf);
-}
-
 int ashmem_create_region(const char* /*ignored*/, size_t size) {
     char pattern[PATH_MAX];
     snprintf(pattern, sizeof(pattern), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
@@ -88,9 +65,22 @@
 int ashmem_get_size_region(int fd)
 {
     struct stat buf;
-    if (!ashmem_validate_stat(fd, &buf)) {
+    int result = fstat(fd, &buf);
+    if (result == -1) {
+        return -1;
+    }
+
+    /*
+     * Check if this is an "ashmem" region.
+     * TODO: This is very hacky, and can easily break.
+     * We need some reliable indicator.
+     */
+    if (!(buf.st_nlink == 0 && S_ISREG(buf.st_mode))) {
+        errno = ENOTTY;
         return -1;
     }
 
     return buf.st_size;
 }
+
+void ashmem_init() {}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 2b39ca6..a5f4f0e 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -39,8 +39,6 @@
 #include <private/android_filesystem_config.h>
 #include <utils/Compat.h>
 
-#include "fs_config.h"
-
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
@@ -48,8 +46,20 @@
 using android::base::EndsWith;
 using android::base::StartsWith;
 
+// 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;
+}
+
 #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
@@ -78,14 +88,14 @@
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "product/bin" },
+    { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
     { 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" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
-    { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/bin" },
+    { 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 },
@@ -107,7 +117,7 @@
 // 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, /system/odm,
-// /system/product or /system/system_ext.
+// /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)
@@ -119,12 +129,15 @@
 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 system_ext_conf_dir[] = "/system_ext/etc/fs_config_dirs";
-static const char system_ext_conf_file[] = "/system_ext/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},
-        {product_conf_file, product_conf_dir}, {system_ext_conf_file, system_ext_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
@@ -156,12 +169,14 @@
     { 00600, AID_ROOT,      AID_ROOT,      0, "product/build.prop" },
     { 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, "system_ext/build.prop" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, system_ext_conf_dir + 1 },
-    { 00444, AID_ROOT,      AID_ROOT,      0, system_ext_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 },
+    { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/debuggerd" },
+    { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
     { 00550, AID_LOGD,      AID_LOGD,      0, "system/bin/logd" },
     { 00700, AID_ROOT,      AID_ROOT,      0, "system/bin/secilc" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
@@ -173,10 +188,9 @@
     { 00550, AID_ROOT,      AID_SHELL,     0, "system/etc/init.ril" },
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/ppp/*" },
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/rc.*" },
-    { 00750, AID_ROOT,      AID_ROOT,      0, "vendor/bin/install-recovery.sh" },
+    { 00440, AID_ROOT,      AID_ROOT,      0, "system/etc/recovery.img" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/build.prop" },
     { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/default.prop" },
-    { 00440, AID_ROOT,      AID_ROOT,      0, "vendor/etc/recovery.img" },
     { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_file + 1 },
 
@@ -203,12 +217,11 @@
     { 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_SHELL,     0, "system/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/apex/*/bin/*" },
-    { 00755, AID_ROOT,      AID_SHELL,     0, "system_ext/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
@@ -247,9 +260,9 @@
 }
 
 // if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>",
-// "system_ext/<stuff>" or "vendor/<stuff>"
+// "product_services/<stuff>" or "vendor/<stuff>"
 static bool is_partition(const std::string& path) {
-    static const char* partitions[] = {"odm/", "oem/", "product/", "system_ext/", "vendor/"};
+    static const char* partitions[] = {"odm/", "oem/", "product/", "product_services/", "vendor/"};
     for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
         if (StartsWith(path, partitions[i])) return true;
     }
@@ -283,19 +296,20 @@
     const int fnm_flags = FNM_NOESCAPE;
     if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true;
 
-    // Check match between logical partition's files and patterns.
-    static constexpr const char* kLogicalPartitions[] = {"system/product/", "system/system_ext/",
-                                                         "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;
-            }
-        }
+    static constexpr const char* kSystem = "system/";
+    if (StartsWith(input, kSystem)) {
+        input.erase(0, strlen(kSystem));
+    } else if (input.size() <= strlen(kSystem)) {
+        return false;
+    } else if (StartsWith(pattern, kSystem)) {
+        pattern.erase(0, strlen(kSystem));
+    } else {
+        return false;
     }
-    return false;
+
+    if (!is_partition(pattern)) return false;
+    if (!is_partition(input)) return false;
+    return fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0;
 }
 #ifndef __ANDROID_VNDK__
 auto __for_testing_only__fs_config_cmp = fs_config_cmp;
@@ -320,7 +334,7 @@
 
         while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) {
             char* prefix;
-            uint16_t host_len = header.len;
+            uint16_t host_len = get2LE((const uint8_t*)&header.len);
             ssize_t len, remainder = host_len - sizeof(header);
             if (remainder <= 0) {
                 ALOGE("%s len is corrupted", conf[which][dir]);
@@ -345,10 +359,10 @@
             if (fs_config_cmp(dir, prefix, len, path, plen)) {
                 free(prefix);
                 close(fd);
-                *uid = header.uid;
-                *gid = header.gid;
-                *mode = (*mode & (~07777)) | header.mode;
-                *capabilities = header.capabilities;
+                *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));
                 return;
             }
             free(prefix);
@@ -366,3 +380,21 @@
     *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
deleted file mode 100644
index 66ad48b..0000000
--- a/libcutils/fs_config.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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
index c6684b4..c26315f 100644
--- a/libcutils/fs_config_test.cpp
+++ b/libcutils/fs_config_test.cpp
@@ -25,8 +25,7 @@
 #include <android-base/strings.h>
 
 #include <private/android_filesystem_config.h>
-
-#include "fs_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;
@@ -46,7 +45,7 @@
         // 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",           false },
+    { 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  },
@@ -56,14 +55,13 @@
     { 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",     false },
-    { false, "odm/bin/wifi",           "vendor/odm/bin/wifi",     true  },
-    { false, "oem/bin/wifi",           "system/oem/bin/wifi",     false },
+    { 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",         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  },
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/jstring.h b/libcutils/include/cutils/jstring.h
new file mode 100644
index 0000000..a342608
--- /dev/null
+++ b/libcutils/include/cutils/jstring.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CUTILS_STRING16_H
+#define __CUTILS_STRING16_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __STDC_VERSION__ < 201112L && __cplusplus < 201103L
+  typedef uint16_t char16_t;
+#endif
+  // otherwise char16_t is a keyword with the right semantics
+
+extern char * strndup16to8 (const char16_t* s, size_t n);
+extern size_t strnlen16to8 (const char16_t* s, size_t n);
+extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n);
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len);
+extern size_t strlen8to16 (const char* utf8Str);
+extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len);
+extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length,
+    size_t *out_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_STRING16_H */
diff --git a/libcutils/include/cutils/sockets.h b/libcutils/include/cutils/sockets.h
index be21a8f..285f150 100644
--- a/libcutils/include/cutils/sockets.h
+++ b/libcutils/include/cutils/sockets.h
@@ -103,6 +103,14 @@
 int socket_close(cutils_socket_t sock);
 
 /*
+ * Sets socket receive timeout using SO_RCVTIMEO. Setting |timeout_ms| to 0
+ * disables receive timeouts.
+ *
+ * Return 0 on success.
+ */
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms);
+
+/*
  * Returns the local port the socket is bound to or -1 on error.
  */
 int socket_get_local_port(cutils_socket_t sock);
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index e1e8230..63c3793 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -28,10 +28,17 @@
  *   mediadrm
  * Whose friendly names do not match the #define statements.
  *
- * This file must only be used for platform (Google managed, and submitted through AOSP), AIDs.  3rd
- * party AIDs must be added via config.fs, which will place them in the corresponding partition's
- * passwd and group files.  There are ranges in this file reserved for AIDs for each 3rd party
- * partition, from which the system reads passwd and group files.
+ * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
+ * can be used to define reserved OEM ranges used for sanity checks
+ * during the build process. The rules are, they must end with START/END
+ * The proper convention is incrementing a number like so:
+ * AID_OEM_RESERVED_START
+ * AID_OEM_RESERVED_1_START
+ * AID_OEM_RESERVED_2_START
+ * ...
+ * The same applies to the END.
+ * They are not required to be in order, but must not overlap each other and
+ * must define a START and END'ing range. START must be smaller than END.
  */
 
 #ifndef _ANDROID_FILESYSTEM_CONFIG_H_
@@ -128,19 +135,14 @@
 #define AID_GPU_SERVICE 1072     /* GPU service daemon */
 #define AID_NETWORK_STACK 1073   /* network stack service */
 #define AID_GSID 1074            /* GSI service daemon */
-#define AID_FSVERITY_CERT 1075   /* fs-verity key ownership in keystore */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
 #define AID_CACHE 2001 /* cache access */
 #define AID_DIAG 2002  /* access to diagnostic resources */
 
-/* The range 2900-2999 is reserved for the vendor partition */
-/* Note that the two 'OEM' ranges pre-dated the vendor partition, so they take the legacy 'OEM'
- * name. Additionally, they pre-dated passwd/group files, so there are users and groups named oem_#
- * created automatically for all values in these ranges.  If there is a user/group in a passwd/group
- * file corresponding to this range, both the oem_# and user/group names will resolve to the same
- * value. */
+/* The range 2900-2999 is reserved for OEM, and must never be
+ * used here */
 #define AID_OEM_RESERVED_START 2900
 #define AID_OEM_RESERVED_END 2999
 
@@ -157,26 +159,10 @@
 #define AID_WAKELOCK 3010     /* Allow system wakelock read/write access */
 #define AID_UHID 3011         /* Allow read/write to /dev/uhid node */
 
-/* The range 5000-5999 is also reserved for vendor partition. */
+/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
 #define AID_OEM_RESERVED_2_START 5000
 #define AID_OEM_RESERVED_2_END 5999
 
-/* The range 6000-6499 is reserved for the system partition. */
-#define AID_SYSTEM_RESERVED_START 6000
-#define AID_SYSTEM_RESERVED_END 6499
-
-/* The range 6500-6999 is reserved for the odm partition. */
-#define AID_ODM_RESERVED_START 6500
-#define AID_ODM_RESERVED_END 6999
-
-/* The range 7000-7499 is reserved for the product partition. */
-#define AID_PRODUCT_RESERVED_START 7000
-#define AID_PRODUCT_RESERVED_END 7499
-
-/* The range 7500-7999 is reserved for the system_ext partition. */
-#define AID_SYSTEM_EXT_RESERVED_START 7500
-#define AID_SYSTEM_EXT_RESERVED_END 7999
-
 #define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
 #define AID_MISC 9998      /* access to misc storage */
 #define AID_NOBODY 9999
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 8a9a1ff..8926491 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -19,17 +19,44 @@
 ** by the device side of adb.
 */
 
-#pragma once
+#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
 
 #include <stdint.h>
 #include <sys/cdefs.h>
+#include <sys/types.h>
 
 #if defined(__BIONIC__)
 #include <linux/capability.h>
 #else  // defined(__BIONIC__)
-#include <private/android_filesystem_capability.h>
+#include "android_filesystem_capability.h"
 #endif  // defined(__BIONIC__)
 
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
+
+/*
+ * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
+ * 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
@@ -47,4 +74,8 @@
 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/jstring.h b/libcutils/include_vndk/cutils/jstring.h
new file mode 120000
index 0000000..f3fd546
--- /dev/null
+++ b/libcutils/include_vndk/cutils/jstring.h
@@ -0,0 +1 @@
+../../include/cutils/jstring.h
\ No newline at end of file
diff --git a/libcutils/sched_policy_test.cpp b/libcutils/sched_policy_test.cpp
index b9e2832..a321c90 100644
--- a/libcutils/sched_policy_test.cpp
+++ b/libcutils/sched_policy_test.cpp
@@ -107,18 +107,6 @@
 
 TEST(SchedPolicy, get_sched_policy_name) {
     EXPECT_STREQ("bg", get_sched_policy_name(SP_BACKGROUND));
-    EXPECT_EQ(nullptr, get_sched_policy_name(SchedPolicy(-2)));
-    EXPECT_EQ(nullptr, get_sched_policy_name(SP_CNT));
-}
-
-TEST(SchedPolicy, get_cpuset_policy_profile_name) {
-    EXPECT_STREQ("CPUSET_SP_BACKGROUND", get_cpuset_policy_profile_name(SP_BACKGROUND));
-    EXPECT_EQ(nullptr, get_cpuset_policy_profile_name(SchedPolicy(-2)));
-    EXPECT_EQ(nullptr, get_cpuset_policy_profile_name(SP_CNT));
-}
-
-TEST(SchedPolicy, get_sched_policy_profile_name) {
-    EXPECT_STREQ("SCHED_SP_BACKGROUND", get_sched_policy_profile_name(SP_BACKGROUND));
-    EXPECT_EQ(nullptr, get_sched_policy_profile_name(SchedPolicy(-2)));
-    EXPECT_EQ(nullptr, get_sched_policy_profile_name(SP_CNT));
+    EXPECT_STREQ("error", get_sched_policy_name(SchedPolicy(-2)));
+    EXPECT_STREQ("error", get_sched_policy_name(SP_CNT));
 }
diff --git a/libcutils/sockets_test.cpp b/libcutils/sockets_test.cpp
index 1fa40bc..b762ac1 100644
--- a/libcutils/sockets_test.cpp
+++ b/libcutils/sockets_test.cpp
@@ -73,6 +73,25 @@
     EXPECT_EQ(0, socket_close(client));
 }
 
+// Tests receive timeout. The timing verification logic must be very coarse to
+// make sure different systems can all pass these tests.
+void TestReceiveTimeout(cutils_socket_t sock) {
+    time_t start_time;
+    char buffer[32];
+
+    // Make sure a 20ms timeout completes in 1 second or less.
+    EXPECT_EQ(0, socket_set_receive_timeout(sock, 20));
+    start_time = time(nullptr);
+    EXPECT_EQ(-1, recv(sock, buffer, sizeof(buffer), 0));
+    EXPECT_LE(difftime(time(nullptr), start_time), 1.0);
+
+    // Make sure a 1250ms timeout takes 1 second or more.
+    EXPECT_EQ(0, socket_set_receive_timeout(sock, 1250));
+    start_time = time(nullptr);
+    EXPECT_EQ(-1, recv(sock, buffer, sizeof(buffer), 0));
+    EXPECT_LE(1.0, difftime(time(nullptr), start_time));
+}
+
 // Tests socket_get_local_port().
 TEST(SocketsTest, TestGetLocalPort) {
     cutils_socket_t server;
@@ -138,6 +157,32 @@
     TestConnectedSockets(handler, client, SOCK_STREAM);
 }
 
+// Tests setting a receive timeout for UDP sockets.
+TEST(SocketsTest, TestUdpReceiveTimeout) {
+    cutils_socket_t sock = socket_inaddr_any_server(0, SOCK_DGRAM);
+    ASSERT_NE(INVALID_SOCKET, sock);
+
+    TestReceiveTimeout(sock);
+
+    EXPECT_EQ(0, socket_close(sock));
+}
+
+// Tests setting a receive timeout for TCP sockets.
+TEST(SocketsTest, TestTcpReceiveTimeout) {
+    cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);
+    ASSERT_NE(INVALID_SOCKET, server);
+
+    cutils_socket_t client = socket_network_client(
+            "localhost", socket_get_local_port(server), SOCK_STREAM);
+    cutils_socket_t handler = accept(server, nullptr, nullptr);
+    EXPECT_EQ(0, socket_close(server));
+
+    TestReceiveTimeout(handler);
+
+    EXPECT_EQ(0, socket_close(client));
+    EXPECT_EQ(0, socket_close(handler));
+}
+
 // Tests socket_send_buffers() failure.
 TEST(SocketsTest, TestSocketSendBuffersFailure) {
     EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0));
diff --git a/libcutils/sockets_unix.cpp b/libcutils/sockets_unix.cpp
index 84663e6..6acdcd8 100644
--- a/libcutils/sockets_unix.cpp
+++ b/libcutils/sockets_unix.cpp
@@ -31,6 +31,13 @@
     return close(sock);
 }
 
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
+    timeval tv;
+    tv.tv_sec = timeout_ms / 1000;
+    tv.tv_usec = (timeout_ms % 1000) * 1000;
+    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+}
+
 ssize_t socket_send_buffers(cutils_socket_t sock,
                             const cutils_socket_buffer_t* buffers,
                             size_t num_buffers) {
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
index 4adb796..df14712 100644
--- a/libcutils/sockets_windows.cpp
+++ b/libcutils/sockets_windows.cpp
@@ -54,6 +54,11 @@
     return closesocket(sock);
 }
 
+int socket_set_receive_timeout(cutils_socket_t sock, int timeout_ms) {
+    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
+                      reinterpret_cast<char*>(&timeout_ms), sizeof(timeout_ms));
+}
+
 ssize_t socket_send_buffers(cutils_socket_t sock,
                             const cutils_socket_buffer_t* buffers,
                             size_t num_buffers) {
diff --git a/libcutils/strdup16to8.cpp b/libcutils/strdup16to8.cpp
new file mode 100644
index 0000000..d89181e
--- /dev/null
+++ b/libcutils/strdup16to8.cpp
@@ -0,0 +1,168 @@
+/* libs/cutils/strdup16to8.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/jstring.h>
+
+#include <assert.h>
+#include <limits.h>  /* for SIZE_MAX */
+#include <stdlib.h>
+
+
+/**
+ * Given a UTF-16 string, compute the length of the corresponding UTF-8
+ * string in bytes.
+ */
+extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
+{
+    size_t utf8Len = 0;
+
+    /* A small note on integer overflow. The result can
+     * potentially be as big as 3*len, which will overflow
+     * for len > SIZE_MAX/3.
+     *
+     * Moreover, the result of a strnlen16to8 is typically used
+     * to allocate a destination buffer to strncpy16to8 which
+     * requires one more byte to terminate the UTF-8 copy, and
+     * this is generally done by careless users by incrementing
+     * the result without checking for integer overflows, e.g.:
+     *
+     *   dst = malloc(strnlen16to8(utf16,len)+1)
+     *
+     * Due to this, the following code will try to detect
+     * overflows, and never return more than (SIZE_MAX-1)
+     * when it detects one. A careless user will try to malloc
+     * SIZE_MAX bytes, which will return NULL which can at least
+     * be detected appropriately.
+     *
+     * As far as I know, this function is only used by strndup16(),
+     * but better be safe than sorry.
+     */
+
+    /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
+     */
+    if (len < (SIZE_MAX-1)/3) {
+        while (len != 0) {
+            len--;
+            unsigned int uic = *utf16Str++;
+
+            if (uic > 0x07ff)
+                utf8Len += 3;
+            else if (uic > 0x7f || uic == 0)
+                utf8Len += 2;
+            else
+                utf8Len++;
+        }
+        return utf8Len;
+    }
+
+    /* The slower but paranoid version */
+    while (len != 0) {
+        len--;
+        unsigned int  uic     = *utf16Str++;
+        size_t        utf8Cur = utf8Len;
+
+        if (uic > 0x07ff)
+            utf8Len += 3;
+        else if (uic > 0x7f || uic == 0)
+            utf8Len += 2;
+        else
+            utf8Len++;
+
+        if (utf8Len < utf8Cur) /* overflow detected */
+            return SIZE_MAX-1;
+    }
+
+    /* don't return SIZE_MAX to avoid common user bug */
+    if (utf8Len == SIZE_MAX)
+        utf8Len = SIZE_MAX-1;
+
+    return utf8Len;
+}
+
+
+/**
+ * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
+ *
+ * This basically means: embedded \0's in the UTF-16 string are encoded
+ * as "0xc0 0x80"
+ *
+ * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
+ * not just "len".
+ *
+ * Please note, a terminated \0 is always added, so your result will always
+ * be "strlen16to8() + 1" bytes long.
+ */
+extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
+{
+    char* utf8cur = utf8Str;
+
+    /* Note on overflows: We assume the user did check the result of
+     * strnlen16to8() properly or at a minimum checked the result of
+     * its malloc(SIZE_MAX) in case of overflow.
+     */
+    while (len != 0) {
+        len--;
+        unsigned int uic = *utf16Str++;
+
+        if (uic > 0x07ff) {
+            *utf8cur++ = (uic >> 12) | 0xe0;
+            *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
+            *utf8cur++ = (uic & 0x3f) | 0x80;
+        } else if (uic > 0x7f || uic == 0) {
+            *utf8cur++ = (uic >> 6) | 0xc0;
+            *utf8cur++ = (uic & 0x3f) | 0x80;
+        } else {
+            *utf8cur++ = uic;
+
+            if (uic == 0) {
+                break;
+            }
+        }
+    }
+
+   *utf8cur = '\0';
+
+   return utf8Str;
+}
+
+/**
+ * Convert a UTF-16 string to UTF-8.
+ *
+ */
+char * strndup16to8 (const char16_t* s, size_t n)
+{
+    if (s == NULL) {
+        return NULL;
+    }
+
+    size_t len = strnlen16to8(s, n);
+
+    /* We are paranoid, and we check for SIZE_MAX-1
+     * too since it is an overflow value for our
+     * strnlen16to8 implementation.
+     */
+    if (len >= SIZE_MAX-1)
+        return NULL;
+
+    char* ret = static_cast<char*>(malloc(len + 1));
+    if (ret == NULL)
+        return NULL;
+
+    strncpy16to8 (ret, s, n);
+
+    return ret;
+}
diff --git a/libcutils/strdup8to16.cpp b/libcutils/strdup8to16.cpp
new file mode 100644
index 0000000..d1e51b9
--- /dev/null
+++ b/libcutils/strdup8to16.cpp
@@ -0,0 +1,215 @@
+/* libs/cutils/strdup8to16.c
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+#include <cutils/jstring.h>
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
+/* See http://www.unicode.org/reports/tr22/ for discussion
+ * on invalid sequences
+ */
+
+#define UTF16_REPLACEMENT_CHAR 0xfffd
+
+/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
+#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> (((ch) >> 3) & 0x1e)) & 3) + 1)
+
+/* note: macro expands to multiple lines */
+#define UTF8_SHIFT_AND_MASK(unicode, byte)  \
+            (unicode)<<=6; (unicode) |= (0x3f & (byte));
+
+#define UNICODE_UPPER_LIMIT 0x10fffd    
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strdup8to16 (const char* s, size_t *out_len)
+{
+    char16_t *ret;
+    size_t len;
+
+    if (s == NULL) return NULL;
+
+    len = strlen8to16(s);
+
+    // fail on overflow
+    if (len && SIZE_MAX/len < sizeof(char16_t))
+        return NULL;
+
+    // no plus-one here. UTF-16 strings are not null terminated
+    ret = (char16_t *) malloc (sizeof(char16_t) * len);
+
+    return strcpy8to16 (ret, s, out_len);
+}
+
+/**
+ * Like "strlen", but for strings encoded with Java's modified UTF-8.
+ *
+ * The value returned is the number of UTF-16 characters required
+ * to represent this string.
+ */
+extern size_t strlen8to16 (const char* utf8Str)
+{
+    size_t len = 0;
+    int ic;
+    int expected = 0;
+
+    while ((ic = *utf8Str++) != '\0') {
+        /* bytes that start 0? or 11 are lead bytes and count as characters.*/
+        /* bytes that start 10 are extention bytes and are not counted */
+         
+        if ((ic & 0xc0) == 0x80) {
+            /* count the 0x80 extention bytes. if we have more than
+             * expected, then start counting them because strcpy8to16
+             * will insert UTF16_REPLACEMENT_CHAR's
+             */
+            expected--;
+            if (expected < 0) {
+                len++;
+            }
+        } else {
+            len++;
+            expected = UTF8_SEQ_LENGTH(ic) - 1;
+
+            /* this will result in a surrogate pair */
+            if (expected == 3) {
+                len++;
+            }
+        }
+    }
+
+    return len;
+}
+
+
+
+/*
+ * Retrieve the next UTF-32 character from a UTF-8 string.
+ *
+ * Stops at inner \0's
+ *
+ * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ */
+static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr)
+{
+    uint32_t ret;
+    int seq_len;
+    int i;
+
+    /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
+    static const unsigned char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
+
+    /* Bytes that start with bits "10" are not leading characters. */
+    if (((**pUtf8Ptr) & 0xc0) == 0x80) {
+        (*pUtf8Ptr)++;
+        return UTF16_REPLACEMENT_CHAR;
+    }
+
+    /* note we tolerate invalid leader 11111xxx here */    
+    seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr);
+
+    ret = (**pUtf8Ptr) & leaderMask [seq_len - 1];
+
+    if (**pUtf8Ptr == '\0') return ret;
+
+    (*pUtf8Ptr)++;
+    for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) {
+        if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR;
+        if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR;
+
+        UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr);
+    }
+
+    return ret;
+}
+
+
+/**
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str, 
+                                       size_t *out_len)
+{   
+    char16_t *dest = utf16Str;
+
+    while (*utf8Str != '\0') {
+        uint32_t ret;
+
+        ret = getUtf32FromUtf8(&utf8Str);
+
+        if (ret <= 0xffff) {
+            *dest++ = (char16_t) ret;
+        } else if (ret <= UNICODE_UPPER_LIMIT)  {
+            /* Create surrogate pairs */
+            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
+        } else {
+            *dest++ = UTF16_REPLACEMENT_CHAR;
+        }
+    }
+
+    *out_len = dest - utf16Str;
+
+    return utf16Str;
+}
+
+/**
+ * length is the number of characters in the UTF-8 string.
+ * out_len is an out parameter (which may not be null) containing the
+ * length of the UTF-16 string (which may contain embedded \0's)
+ */
+
+extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str,
+                                       int length, size_t *out_len)
+{
+    /* TODO: Share more of this code with the method above. Only 2 lines changed. */
+    
+    char16_t *dest = utf16Str;
+
+    const char *end = utf8Str + length; /* This line */
+    while (utf8Str < end) {             /* and this line changed. */
+        uint32_t ret;
+
+        ret = getUtf32FromUtf8(&utf8Str);
+
+        if (ret <= 0xffff) {
+            *dest++ = (char16_t) ret;
+        } else if (ret <= UNICODE_UPPER_LIMIT)  {
+            /* Create surrogate pairs */
+            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
+
+            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
+            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
+        } else {
+            *dest++ = UTF16_REPLACEMENT_CHAR;
+        }
+    }
+
+    *out_len = dest - utf16Str;
+
+    return utf16Str;
+}
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index bff16c1..4da8215 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -34,9 +34,12 @@
     if (atrace_marker_fd == -1) {
         ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
         atrace_enabled_tags = 0;
-    } else {
-      atrace_enabled_tags = atrace_get_property();
+        goto done;
     }
+
+    atrace_enabled_tags = atrace_get_property();
+
+done:
     atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
 }
 
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index ce0c3c8..d27feb9 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -20,7 +20,12 @@
         "-Werror",
     ],
     cppflags: [
-        "-Wextra",
+        "-Weverything",
+        "-Wno-c++98-compat-pedantic",
+        // Hide errors in headers we include
+        "-Wno-global-constructors",
+        "-Wno-exit-time-destructors",
+        "-Wno-padded",
     ],
     srcs: ["GrallocUsageConversion.cpp"],
     export_include_dirs: ["include"],
diff --git a/libion/OWNERS b/libion/OWNERS
deleted file mode 100644
index 143ad2d..0000000
--- a/libion/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-sspatil@google.com
-hridya@google.com
diff --git a/libion/include/ion/ion.h b/libion/include/ion/ion.h
index 1480bd9..a60d24e 100644
--- a/libion/include/ion/ion.h
+++ b/libion/include/ion/ion.h
@@ -49,7 +49,6 @@
 int ion_query_get_heaps(int fd, int cnt, void* buffers);
 
 int ion_is_legacy(int fd);
-int ion_is_using_modular_heaps(int fd);
 
 __END_DECLS
 
diff --git a/libion/ion.c b/libion/ion.c
index 07b4caf..b8de5a4 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -31,12 +31,10 @@
 #include <unistd.h>
 
 #include <ion/ion.h>
-#include <linux/ion_4.19.h>
+#include "ion_4.12.h"
 
 #include <log/log.h>
 
-#define ION_ABI_VERSION_MODULAR_HEAPS 2
-
 enum ion_version { ION_VERSION_UNKNOWN, ION_VERSION_MODERN, ION_VERSION_LEGACY };
 
 static atomic_int g_ion_version = ATOMIC_VAR_INIT(ION_VERSION_UNKNOWN);
@@ -77,14 +75,6 @@
     return ret;
 }
 
-int ion_is_using_modular_heaps(int fd) {
-    int ion_abi_version = 0;
-    int ret = 0;
-
-    ret = ion_ioctl(fd, ION_IOC_ABI_VERSION, &ion_abi_version);
-    return (ret == 0 && ion_abi_version >= ION_ABI_VERSION_MODULAR_HEAPS);
-}
-
 int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, unsigned int flags,
               ion_user_handle_t* handle) {
     int ret = 0;
@@ -162,8 +152,6 @@
     ion_user_handle_t handle;
     int ret;
 
-    if (!handle_fd) return -EINVAL;
-
     if (!ion_is_legacy(fd)) {
         struct ion_new_allocation_data data = {
             .len = len,
@@ -213,7 +201,6 @@
     int ret;
     struct ion_heap_query query;
 
-    if (!cnt) return -EINVAL;
     memset(&query, 0, sizeof(query));
 
     ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
diff --git a/libion/original-kernel-headers/linux/ion_4.12.h b/libion/ion_4.12.h
similarity index 100%
rename from libion/original-kernel-headers/linux/ion_4.12.h
rename to libion/ion_4.12.h
diff --git a/libion/kernel-headers/linux/ion_4.12.h b/libion/kernel-headers/linux/ion_4.12.h
deleted file mode 100644
index 1af8284..0000000
--- a/libion/kernel-headers/linux/ion_4.12.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/****************************************************************************
- ****************************************************************************
- ***
- ***   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)
-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 {
-    char name[MAX_HEAP_NAME];
-    __u32 type;
-    __u32 heap_id;
-    __u32 reserved0;
-    __u32 reserved1;
-    __u32 reserved2;
-};
-struct ion_heap_query {
-    __u32 cnt;
-    __u32 reserved0;
-    __u64 heaps;
-    __u32 reserved1;
-    __u32 reserved2;
-};
-#define ION_IOC_MAGIC 'I'
-#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
-#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
-#endif
diff --git a/libion/kernel-headers/linux/ion_4.19.h b/libion/kernel-headers/linux/ion_4.19.h
deleted file mode 100644
index f5b59f1..0000000
--- a/libion/kernel-headers/linux/ion_4.19.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
- ****************************************************************************
- ***
- ***   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)
-enum ion_heap_type_ext {
-    ION_HEAP_TYPE_CUSTOM_EXT = 16,
-    ION_HEAP_TYPE_MAX = 31,
-};
-enum ion_heap_id {
-    ION_HEAP_SYSTEM = (1 << ION_HEAP_TYPE_SYSTEM),
-    ION_HEAP_SYSTEM_CONTIG = (ION_HEAP_SYSTEM << 1),
-    ION_HEAP_CARVEOUT_START = (ION_HEAP_SYSTEM_CONTIG << 1),
-    ION_HEAP_CARVEOUT_END = (ION_HEAP_CARVEOUT_START << 4),
-    ION_HEAP_CHUNK = (ION_HEAP_CARVEOUT_END << 1),
-    ION_HEAP_DMA_START = (ION_HEAP_CHUNK << 1),
-    ION_HEAP_DMA_END = (ION_HEAP_DMA_START << 7),
-    ION_HEAP_CUSTOM_START = (ION_HEAP_DMA_END << 1),
-    ION_HEAP_CUSTOM_END = (ION_HEAP_CUSTOM_START << 15),
-};
-#define ION_NUM_MAX_HEAPS (32)
-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 {
-    char name[MAX_HEAP_NAME];
-    __u32 type;
-    __u32 heap_id;
-    __u32 reserved0;
-    __u32 reserved1;
-    __u32 reserved2;
-};
-struct ion_heap_query {
-    __u32 cnt;
-    __u32 reserved0;
-    __u64 heaps;
-    __u32 reserved1;
-    __u32 reserved2;
-};
-#define ION_IOC_MAGIC 'I'
-#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
-#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
-#define ION_IOC_ABI_VERSION _IOR(ION_IOC_MAGIC, 9, __u32)
-#endif
diff --git a/libion/original-kernel-headers/linux/ion_4.19.h b/libion/original-kernel-headers/linux/ion_4.19.h
deleted file mode 100644
index 75fef39..0000000
--- a/libion/original-kernel-headers/linux/ion_4.19.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Adapted from drivers/staging/android/uapi/ion.h
- *
- * Copyright (C) 2019 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)
-
-enum ion_heap_type_ext {
-    ION_HEAP_TYPE_CUSTOM_EXT = 16,
-    ION_HEAP_TYPE_MAX = 31,
-};
-
-/**
- * ion_heap_id - list of standard heap ids that Android can use
- *
- * @ION_HEAP_SYSTEM		Id for the ION_HEAP_TYPE_SYSTEM
- * @ION_HEAP_SYSTEM_CONTIG	Id for the ION_HEAP_TYPE_SYSTEM_CONTIG
- * @ION_HEAP_CHUNK		Id for the ION_HEAP_TYPE_CHUNK
- * @ION_HEAP_CARVEOUT_START	Start of reserved id range for heaps of type
- *				ION_HEAP_TYPE_CARVEOUT
- * @ION_HEAP_CARVEOUT_END	End of reserved id range for heaps of type
- *				ION_HEAP_TYPE_CARVEOUT
- * @ION_HEAP_DMA_START 		Start of reserved id range for heaps of type
- *				ION_HEAP_TYPE_DMA
- * @ION_HEAP_DMA_END		End of reserved id range for heaps of type
- *				ION_HEAP_TYPE_DMA
- * @ION_HEAP_CUSTOM_START	Start of reserved id range for heaps of custom
- *				type
- * @ION_HEAP_CUSTOM_END		End of reserved id range for heaps of custom
- *				type
- */
-enum ion_heap_id {
-    ION_HEAP_SYSTEM = (1 << ION_HEAP_TYPE_SYSTEM),
-    ION_HEAP_SYSTEM_CONTIG = (ION_HEAP_SYSTEM << 1),
-    ION_HEAP_CARVEOUT_START = (ION_HEAP_SYSTEM_CONTIG << 1),
-    ION_HEAP_CARVEOUT_END = (ION_HEAP_CARVEOUT_START << 4),
-    ION_HEAP_CHUNK = (ION_HEAP_CARVEOUT_END << 1),
-    ION_HEAP_DMA_START = (ION_HEAP_CHUNK << 1),
-    ION_HEAP_DMA_END = (ION_HEAP_DMA_START << 7),
-    ION_HEAP_CUSTOM_START = (ION_HEAP_DMA_END << 1),
-    ION_HEAP_CUSTOM_END = (ION_HEAP_CUSTOM_START << 15),
-};
-
-#define ION_NUM_MAX_HEAPS (32)
-
-/**
- * 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)
-
-/**
- * DOC: ION_IOC_HEAP_ABI_VERSION - return ABI version
- *
- * Returns ABI version for this driver
- */
-#define ION_IOC_ABI_VERSION _IOR(ION_IOC_MAGIC, 9, __u32)
-
-#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index 989e029..b3fcb3b 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -18,17 +18,18 @@
     name: "ion-unit-tests",
     cflags: [
         "-g",
+        "-Wall",
+        "-Werror",
         "-Wno-missing-field-initializers",
     ],
     shared_libs: ["libion"],
     srcs: [
-        "allocate_test.cpp",
-        "exit_test.cpp",
-        "heap_query.cpp",
-        "system_heap.cpp",
-        "invalid_values_test.cpp",
         "ion_test_fixture.cpp",
+        "allocate_test.cpp",
+        "formerly_valid_handle_test.cpp",
+        "invalid_values_test.cpp",
         "map_test.cpp",
-        "modular_heap_check.cpp",
+        "device_test.cpp",
+        "exit_test.cpp",
     ],
 }
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index 5ed01bb..3c4524e 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,106 +14,95 @@
  * limitations under the License.
  */
 
-#include <sys/mman.h>
 #include <memory>
+#include <sys/mman.h>
 
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
 #include "ion_test_fixture.h"
 
-class Allocate : public IonTest {};
+class Allocate : public IonAllHeapsTest {
+};
 
-TEST_F(Allocate, Allocate) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Allocate, Allocate)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            int fd;
-            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &fd));
-            ASSERT_TRUE(fd != 0);
-            ASSERT_EQ(close(fd), 0);  // free the buffer
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
         }
     }
 }
 
-TEST_F(Allocate, AllocateCached) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Allocate, AllocateCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            int fd;
-            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), ION_FLAG_CACHED, &fd));
-            ASSERT_TRUE(fd != 0);
-            ASSERT_EQ(close(fd), 0);  // free the buffer
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
         }
     }
 }
 
-TEST_F(Allocate, AllocateCachedNeedsSync) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Allocate, AllocateCachedNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            int fd;
-            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
-                                      ION_FLAG_CACHED_NEEDS_SYNC, &fd));
-            ASSERT_TRUE(fd != 0);
-            ASSERT_EQ(close(fd), 0);  // free the buffer
+            ion_user_handle_t handle = 0;
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED_NEEDS_SYNC, &handle));
+            ASSERT_TRUE(handle != 0);
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
         }
     }
 }
 
-TEST_F(Allocate, RepeatedAllocate) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Allocate, RepeatedAllocate)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            int fd;
+            ion_user_handle_t handle = 0;
 
             for (unsigned int i = 0; i < 1024; i++) {
                 SCOPED_TRACE(::testing::Message() << "iteration " << i);
-                ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &fd));
-                ASSERT_TRUE(fd != 0);
-                ASSERT_EQ(close(fd), 0);  // free the buffer
+                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+                ASSERT_TRUE(handle != 0);
+                ASSERT_EQ(0, ion_free(m_ionFd, handle));
             }
         }
     }
 }
 
-TEST_F(Allocate, Large) {
-    for (const auto& heap : ion_heaps) {
-        SCOPED_TRACE(::testing::Message()
-                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
-        int fd;
-        ASSERT_EQ(-ENOMEM,
-                  ion_alloc_fd(ionfd, 3UL * 1024 * 1024 * 1024, 0, (1 << heap.heap_id), 0, &fd));
-    }
-}
-
-// Make sure all heaps always return zeroed pages
-TEST_F(Allocate, Zeroed) {
+TEST_F(Allocate, Zeroed)
+{
     auto zeroes_ptr = std::make_unique<char[]>(4096);
 
-    for (const auto& heap : ion_heaps) {
-        SCOPED_TRACE(::testing::Message()
-                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
         int fds[16];
         for (unsigned int i = 0; i < 16; i++) {
             int map_fd = -1;
 
-            ASSERT_EQ(0, ion_alloc_fd(ionfd, 4096, 0, (1 << heap.heap_id), 0, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void* ptr = NULL;
+            void *ptr = NULL;
             ptr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
 
@@ -127,13 +116,13 @@
             ASSERT_EQ(0, close(fds[i]));
         }
 
-        int new_ionfd = ion_open();
+        int newIonFd = ion_open();
         int map_fd = -1;
 
-        ASSERT_EQ(0, ion_alloc_fd(new_ionfd, 4096, 0, (1 << heap.heap_id), 0, &map_fd));
+        ASSERT_EQ(0, ion_alloc_fd(newIonFd, 4096, 0, heapMask, 0, &map_fd));
         ASSERT_GE(map_fd, 0);
 
-        void* ptr = NULL;
+        void *ptr = NULL;
         ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
         ASSERT_TRUE(ptr != NULL);
 
@@ -141,6 +130,14 @@
 
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
-        ASSERT_EQ(0, ion_close(new_ionfd));
+    }
+}
+
+TEST_F(Allocate, Large)
+{
+    for (unsigned int heapMask : m_allHeaps) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        ion_user_handle_t handle = 0;
+        ASSERT_EQ(-ENOMEM, ion_alloc(m_ionFd, 3UL*1024*1024*1024, 0, heapMask, 0, &handle));
     }
 }
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
new file mode 100644
index 0000000..eb3f7b6
--- /dev/null
+++ b/libion/tests/device_test.cpp
@@ -0,0 +1,546 @@
+/*
+ * 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 <fcntl.h>
+#include <memory>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/ion_test.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+#define ALIGN(x,y) (((x) + ((y) - 1)) & ~((y) - 1))
+
+class Device : public IonAllHeapsTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    int m_deviceFd;
+    void readDMA(int fd, void *buf, size_t size);
+    void writeDMA(int fd, void *buf, size_t size);
+    void readKernel(int fd, void *buf, size_t size);
+    void writeKernel(int fd, void *buf, size_t size);
+    void blowCache();
+    void dirtyCache(void *ptr, size_t size);
+};
+
+void Device::SetUp()
+{
+    IonAllHeapsTest::SetUp();
+    m_deviceFd = open("/dev/ion-test", O_RDONLY);
+    ASSERT_GE(m_deviceFd, 0);
+}
+
+void Device::TearDown()
+{
+    ASSERT_EQ(0, close(m_deviceFd));
+    IonAllHeapsTest::TearDown();
+}
+
+void Device::readDMA(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 0,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::writeDMA(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 1,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::readKernel(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 0,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::writeKernel(int fd, void *buf, size_t size)
+{
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
+    struct ion_test_rw_data ion_test_rw_data = {
+            .ptr = (uint64_t)buf,
+            .offset = 0,
+            .size = size,
+            .write = 1,
+    };
+
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
+    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
+}
+
+void Device::blowCache()
+{
+    const size_t bigger_than_cache = 8*1024*1024;
+    void *buf1 = malloc(bigger_than_cache);
+    void *buf2 = malloc(bigger_than_cache);
+    memset(buf1, 0xaa, bigger_than_cache);
+    memcpy(buf2, buf1, bigger_than_cache);
+    free(buf1);
+    free(buf2);
+}
+
+void Device::dirtyCache(void *ptr, size_t size)
+{
+    /* try to dirty cache lines */
+    for (size_t i = size-1; i > 0; i--) {
+        ((volatile char *)ptr)[i];
+        ((char *)ptr)[i] = i;
+    }
+}
+
+TEST_F(Device, KernelReadCached)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, KernelWriteCached)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, DMAReadCached)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, DMAWriteCached)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, KernelReadCachedNeedsSync)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, KernelWriteCachedNeedsSync)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, DMAReadCachedNeedsSync)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ion_sync_fd(m_ionFd, map_fd);
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, DMAWriteCachedNeedsSync)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        ion_sync_fd(m_ionFd, map_fd);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+TEST_F(Device, KernelRead)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        ((char*)buf)[4096] = 0x12;
+        readKernel(map_fd, buf, 4096);
+        ASSERT_EQ(((char*)buf)[4096], 0x12);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, KernelWrite)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeKernel(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, DMARead)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        for (int i = 0; i < 4096; i++)
+            ((char *)ptr)[i] = i;
+
+        readDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)buf)[i]);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, DMAWrite)
+{
+    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
+
+    for (int i = 0; i < 4096; i++)
+        ((char *)buf)[i] = i;
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = 0;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        writeDMA(map_fd, buf, 4096);
+
+        for (int i = 0; i < 4096; i++)
+            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
+
+TEST_F(Device, IsCached)
+{
+    auto buf_ptr = std::make_unique<char[]>(4096);
+    void *buf = buf_ptr.get();
+
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        int map_fd = -1;
+        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
+
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
+        ASSERT_GE(map_fd, 0);
+
+        void *ptr;
+        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        ASSERT_TRUE(ptr != NULL);
+
+        dirtyCache(ptr, 4096);
+
+        readDMA(map_fd, buf, 4096);
+
+        bool same = true;
+        for (int i = 4096-16; i >= 0; i -= 16)
+            if (((char *)buf)[i] != i)
+                same = false;
+        ASSERT_FALSE(same);
+
+        ASSERT_EQ(0, munmap(ptr, 4096));
+        ASSERT_EQ(0, close(map_fd));
+    }
+}
diff --git a/libion/tests/exit_test.cpp b/libion/tests/exit_test.cpp
index f312389..cdd3e27 100644
--- a/libion/tests/exit_test.cpp
+++ b/libion/tests/exit_test.cpp
@@ -22,206 +22,206 @@
 
 #include "ion_test_fixture.h"
 
-class Exit : public IonTest {};
+class Exit : public IonAllHeapsTest {
+};
 
-TEST_F(Exit, WithAllocFd) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Exit, WithAlloc)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT(
-                    {
-                        int handle_fd = -1;
+            EXPECT_EXIT({
+                ion_user_handle_t handle = 0;
 
-                        ASSERT_EQ(0,
-                                  ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &handle_fd));
-                        ASSERT_NE(-1, handle_fd);
-                        exit(0);
-                    },
-                    ::testing::ExitedWithCode(0), "");
+                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+                ASSERT_TRUE(handle != 0);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithRepeatedAllocFd) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Exit, WithAllocFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int handle_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
+                ASSERT_NE(-1, handle_fd);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithRepeatedAllocFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
             for (unsigned int i = 0; i < 1024; i++) {
-                SCOPED_TRACE(::testing::Message()
-                             << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+                SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
                 SCOPED_TRACE(::testing::Message() << "size " << size);
-                ASSERT_EXIT(
-                        {
-                            int handle_fd = -1;
+                ASSERT_EXIT({
+                    int handle_fd = -1;
 
-                            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0,
-                                                      &handle_fd));
-                            ASSERT_NE(-1, handle_fd);
-                            exit(0);
-                        },
-                        ::testing::ExitedWithCode(0), "")
-                        << "failed on heap " << heap.name << ":" << heap.type << ":" << heap.heap_id
-                        << " and size " << size << " on iteration " << i;
+                    ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
+                    ASSERT_NE(-1, handle_fd);
+                    exit(0);
+                }, ::testing::ExitedWithCode(0), "")
+                        << "failed on heap " << heapMask
+                        << " and size " << size
+                        << " on iteration " << i;
             }
         }
     }
 }
 
-TEST_F(Exit, WithMapping) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+
+TEST_F(Exit, WithMapping)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT(
-                    {
-                        int map_fd = -1;
+            EXPECT_EXIT({
+                int map_fd = -1;
 
-                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
-                        ASSERT_GE(map_fd, 0);
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+                ASSERT_GE(map_fd, 0);
 
-                        void* ptr;
-                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                        ASSERT_TRUE(ptr != NULL);
-                        exit(0);
-                    },
-                    ::testing::ExitedWithCode(0), "");
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
+
+}
+
+TEST_F(Exit, WithPartialMapping)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithPartialMapping) {
-    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Exit, WithMappingCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT(
-                    {
-                        int map_fd = -1;
+            EXPECT_EXIT({
+                int map_fd = -1;
 
-                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
-                        ASSERT_GE(map_fd, 0);
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
+                ASSERT_GE(map_fd, 0);
 
-                        void* ptr;
-                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                        ASSERT_TRUE(ptr != NULL);
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
+        }
+    }
 
-                        ASSERT_EQ(0, munmap(ptr, size / 2));
-                        exit(0);
-                    },
-                    ::testing::ExitedWithCode(0), "");
+}
+
+TEST_F(Exit, WithPartialMappingCached)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT({
+                int map_fd = -1;
+
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
+                ASSERT_GE(map_fd, 0);
+
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithMappingCached) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Exit, WithMappingNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT(
-                    {
-                        int map_fd = -1;
+            EXPECT_EXIT({
+                int map_fd = -1;
 
-                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
-                                                  ION_FLAG_CACHED, &map_fd));
-                        ASSERT_GE(map_fd, 0);
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
+                ASSERT_GE(map_fd, 0);
 
-                        void* ptr;
-                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                        ASSERT_TRUE(ptr != NULL);
-                        exit(0);
-                    },
-                    ::testing::ExitedWithCode(0), "");
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
         }
     }
+
 }
 
-TEST_F(Exit, WithPartialMappingCached) {
-    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Exit, WithPartialMappingNeedsSync)
+{
+    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT(
-                    {
-                        int map_fd = -1;
+            EXPECT_EXIT({
+                int map_fd = -1;
 
-                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
-                                                  ION_FLAG_CACHED, &map_fd));
-                        ASSERT_GE(map_fd, 0);
+                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
+                ASSERT_GE(map_fd, 0);
 
-                        void* ptr;
-                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                        ASSERT_TRUE(ptr != NULL);
+                void *ptr;
+                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                ASSERT_TRUE(ptr != NULL);
 
-                        ASSERT_EQ(0, munmap(ptr, size / 2));
-                        exit(0);
-                    },
-                    ::testing::ExitedWithCode(0), "");
-        }
-    }
-}
-
-TEST_F(Exit, WithMappingNeedsSync) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT(
-                    {
-                        int map_fd = -1;
-
-                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
-                                                  ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
-                                                  &map_fd));
-                        ASSERT_GE(map_fd, 0);
-
-                        void* ptr;
-                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                        ASSERT_TRUE(ptr != NULL);
-                        exit(0);
-                    },
-                    ::testing::ExitedWithCode(0), "");
-        }
-    }
-}
-
-TEST_F(Exit, WithPartialMappingNeedsSync) {
-    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT(
-                    {
-                        int map_fd = -1;
-
-                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
-                                                  ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
-                                                  &map_fd));
-                        ASSERT_GE(map_fd, 0);
-
-                        void* ptr;
-                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                        ASSERT_TRUE(ptr != NULL);
-
-                        ASSERT_EQ(0, munmap(ptr, size / 2));
-                        exit(0);
-                    },
-                    ::testing::ExitedWithCode(0), "");
+                ASSERT_EQ(0, munmap(ptr, size / 2));
+                exit(0);
+            }, ::testing::ExitedWithCode(0), "");
         }
     }
 }
diff --git a/libion/tests/formerly_valid_handle_test.cpp b/libion/tests/formerly_valid_handle_test.cpp
new file mode 100644
index 0000000..01ab8f3
--- /dev/null
+++ b/libion/tests/formerly_valid_handle_test.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 <sys/mman.h>
+
+#include <gtest/gtest.h>
+
+#include <ion/ion.h>
+
+#include "ion_test_fixture.h"
+
+class FormerlyValidHandle : public IonTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    ion_user_handle_t m_handle;
+};
+
+void FormerlyValidHandle::SetUp()
+{
+    IonTest::SetUp();
+    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, 1/* ion_env->m_firstHeap */, 0, &m_handle));
+    ASSERT_TRUE(m_handle != 0);
+    ASSERT_EQ(0, ion_free(m_ionFd, m_handle));
+}
+
+void FormerlyValidHandle::TearDown()
+{
+    m_handle = 0;
+}
+
+TEST_F(FormerlyValidHandle, free)
+{
+	ASSERT_EQ(-EINVAL, ion_free(m_ionFd, m_handle));
+}
+
+TEST_F(FormerlyValidHandle, map)
+{
+    int map_fd;
+    unsigned char *ptr;
+
+    ASSERT_EQ(-EINVAL, ion_map(m_ionFd, m_handle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+}
+
+TEST_F(FormerlyValidHandle, share)
+{
+    int share_fd;
+
+    ASSERT_EQ(-EINVAL, ion_share(m_ionFd, m_handle, &share_fd));
+}
diff --git a/libion/tests/heap_query.cpp b/libion/tests/heap_query.cpp
deleted file mode 100644
index fed8030..0000000
--- a/libion/tests/heap_query.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 <ion/ion.h>
-#include "ion_test_fixture.h"
-
-class HeapQuery : public IonTest {};
-
-TEST_F(HeapQuery, AtleastOneHeap) {
-    ASSERT_GT(ion_heaps.size(), 0);
-}
-
-// TODO: Adjust this test to account for the range of valid carveout and DMA heap ids.
-TEST_F(HeapQuery, HeapIdVerify) {
-    for (const auto& heap : ion_heaps) {
-        SCOPED_TRACE(::testing::Message() << "Invalid id for heap:" << heap.name << ":" << heap.type
-                                          << ":" << heap.heap_id);
-        switch (heap.type) {
-            case ION_HEAP_TYPE_SYSTEM:
-                ASSERT_TRUE((1 << heap.heap_id) & ION_HEAP_SYSTEM_MASK);
-                break;
-            case ION_HEAP_TYPE_SYSTEM_CONTIG:
-                ASSERT_TRUE((1 << heap.heap_id) & ION_HEAP_SYSTEM_CONTIG_MASK);
-                break;
-            case ION_HEAP_TYPE_CARVEOUT:
-                ASSERT_TRUE((1 << heap.heap_id) & ION_HEAP_CARVEOUT_MASK);
-                break;
-            case ION_HEAP_TYPE_DMA:
-                ASSERT_TRUE((1 << heap.heap_id) & ION_HEAP_TYPE_DMA_MASK);
-                break;
-        }
-    }
-}
diff --git a/libion/tests/invalid_values_test.cpp b/libion/tests/invalid_values_test.cpp
index 48fcd72..77fea17 100644
--- a/libion/tests/invalid_values_test.cpp
+++ b/libion/tests/invalid_values_test.cpp
@@ -16,71 +16,171 @@
 
 #include <sys/mman.h>
 
-#include <memory>
-#include <vector>
-
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
+
 #include "ion_test_fixture.h"
 
-class InvalidValues : public IonTest {};
+class InvalidValues : public IonAllHeapsTest {
+ public:
+    virtual void SetUp();
+    virtual void TearDown();
+    ion_user_handle_t m_validHandle;
+    int m_validShareFd;
+    ion_user_handle_t const m_badHandle = -1;
+};
 
-TEST_F(InvalidValues, ion_close) {
+void InvalidValues::SetUp()
+{
+    IonAllHeapsTest::SetUp();
+    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, m_firstHeap, 0, &m_validHandle))
+      << m_ionFd << " " << m_firstHeap;
+    ASSERT_TRUE(m_validHandle != 0);
+    ASSERT_EQ(0, ion_share(m_ionFd, m_validHandle, &m_validShareFd));
+}
+
+void InvalidValues::TearDown()
+{
+    ASSERT_EQ(0, ion_free(m_ionFd, m_validHandle));
+    ASSERT_EQ(0, close(m_validShareFd));
+    m_validHandle = 0;
+    IonAllHeapsTest::TearDown();
+}
+
+TEST_F(InvalidValues, ion_close)
+{
     EXPECT_EQ(-EBADF, ion_close(-1));
 }
 
-TEST_F(InvalidValues, ion_alloc_fd) {
-    int fd;
-    // no heaps
-    EXPECT_EQ(-ENODEV, ion_alloc_fd(ionfd, 4096, 0, 0, 0, &fd));
-    for (const auto& heap : ion_heaps) {
-        // invalid ion_fd
-        int ret = ion_alloc_fd(0, 4096, 0, (1 << heap.heap_id), 0, &fd);
-        EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-        // invalid ion_fd
-        EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, (1 << heap.heap_id), 0, &fd));
-        SCOPED_TRACE(::testing::Message()
-                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
-        // zero size
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 0, 0, (1 << heap.heap_id), 0, &fd));
-        // too large size
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, -1, 0, (1 << heap.heap_id), 0, &fd));
-        // bad alignment
-        // TODO: Current userspace and kernel code completely ignores alignment. So this
-        // test is going to fail. We need to completely remove alignment from the API.
-        // All memory by default is always page aligned. OR actually pass the alignment
-        // down into the kernel and make kernel respect the alignment.
-        // EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 4096, -1, (1 << heap.heap_id), 0, &fd));
-
-        // NULL fd
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 4096, 0, (1 << heap.heap_id), 0, nullptr));
+TEST_F(InvalidValues, ion_alloc)
+{
+    ion_user_handle_t handle;
+    /* invalid ion_fd */
+    int ret = ion_alloc(0, 4096, 0, m_firstHeap, 0, &handle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion_fd */
+    EXPECT_EQ(-EBADF, ion_alloc(-1, 4096, 0, m_firstHeap, 0, &handle));
+    /* no heaps */
+    EXPECT_EQ(-ENODEV, ion_alloc(m_ionFd, 4096, 0, 0, 0, &handle));
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        /* zero size */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 0, 0, heapMask, 0, &handle));
+        /* too large size */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, -1, 0, heapMask, 0, &handle));
+        /* bad alignment */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, -1, heapMask, 0, &handle));
+        /* NULL handle */
+        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, 0, heapMask, 0, NULL));
     }
 }
 
-TEST_F(InvalidValues, ion_query_heap_cnt) {
-    // NULL count
-    EXPECT_EQ(-EINVAL, ion_query_heap_cnt(ionfd, nullptr));
-
-    int heap_count;
-    // bad fd
-    EXPECT_EQ(-EBADF, ion_query_heap_cnt(-1, &heap_count));
+TEST_F(InvalidValues, ion_alloc_fd)
+{
+    int fd;
+    /* invalid ion_fd */
+    int ret = ion_alloc_fd(0, 4096, 0, m_firstHeap, 0, &fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion_fd */
+    EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, m_firstHeap, 0, &fd));
+    /* no heaps */
+    EXPECT_EQ(-ENODEV, ion_alloc_fd(m_ionFd, 4096, 0, 0, 0, &fd));
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+        /* zero size */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 0, 0, heapMask, 0, &fd));
+        /* too large size */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, -1, 0, heapMask, 0, &fd));
+        /* bad alignment */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, -1, heapMask, 0, &fd));
+        /* NULL handle */
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, NULL));
+    }
 }
 
-TEST_F(InvalidValues, ion_query_get_heaps) {
-    int heap_count;
-    ASSERT_EQ(0, ion_query_heap_cnt(ionfd, &heap_count));
-    ASSERT_GT(heap_count, 0);
+TEST_F(InvalidValues, ion_free)
+{
+    /* invalid ion fd */
+    int ret = ion_free(0, m_validHandle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_free(-1, m_validHandle));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, 0));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, m_badHandle));
+}
 
-    // nullptr buffers, still returns success but without
-    // the ion_heap_data.
-    EXPECT_EQ(0, ion_query_get_heaps(ionfd, heap_count, nullptr));
+TEST_F(InvalidValues, ion_map)
+{
+    int map_fd;
+    unsigned char *ptr;
 
-    std::unique_ptr<struct ion_heap_data[]> heaps =
-            std::make_unique<struct ion_heap_data[]>(heap_count);
-    // bad fd
-    EXPECT_EQ(-EBADF, ion_query_get_heaps(-1, heap_count, heaps.get()));
+    /* invalid ion fd */
+    int ret = ion_map(0, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_map(-1, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, 0, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_badHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* zero length */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 0, PROT_READ, 0, 0, &ptr, &map_fd));
+    /* bad prot */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, -1, 0, 0, &ptr, &map_fd));
+    /* bad offset */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, -1, &ptr, &map_fd));
+    /* NULL ptr */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, NULL, &map_fd));
+    /* NULL map_fd */
+    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, NULL));
+}
 
-    // invalid heap data pointer
-    EXPECT_EQ(-EFAULT, ion_query_get_heaps(ionfd, heap_count, reinterpret_cast<void*>(0xdeadf00d)));
+TEST_F(InvalidValues, ion_share)
+{
+    int share_fd;
+
+    /* invalid ion fd */
+    int ret = ion_share(0, m_validHandle, &share_fd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_share(-1, m_validHandle, &share_fd));
+    /* zero handle */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, 0, &share_fd));
+    /* bad handle */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_badHandle, &share_fd));
+    /* NULL share_fd */
+    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_validHandle, NULL));
+}
+
+TEST_F(InvalidValues, ion_import)
+{
+    ion_user_handle_t handle;
+
+    /* invalid ion fd */
+    int ret = ion_import(0, m_validShareFd, &handle);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_import(-1, m_validShareFd, &handle));
+    /* bad share_fd */
+    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, 0, &handle));
+    /* invalid share_fd */
+    EXPECT_EQ(-EBADF, ion_import(m_ionFd, -1, &handle));
+    /* NULL handle */
+    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, m_validShareFd, NULL));
+}
+
+TEST_F(InvalidValues, ion_sync_fd)
+{
+    /* invalid ion fd */
+    int ret = ion_sync_fd(0, m_validShareFd);
+    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+    /* invalid ion fd */
+    EXPECT_EQ(-EBADF, ion_sync_fd(-1, m_validShareFd));
+    /* bad share_fd */
+    EXPECT_EQ(-EINVAL, ion_sync_fd(m_ionFd, 0));
+    /* invalid share_fd */
+    EXPECT_EQ(-EBADF, ion_sync_fd(m_ionFd, -1));
 }
diff --git a/libion/tests/ion_test_fixture.cpp b/libion/tests/ion_test_fixture.cpp
index 935fe5c..e20c730 100644
--- a/libion/tests/ion_test_fixture.cpp
+++ b/libion/tests/ion_test_fixture.cpp
@@ -15,26 +15,59 @@
  */
 
 #include <gtest/gtest.h>
+
 #include <ion/ion.h>
 
 #include "ion_test_fixture.h"
 
-IonTest::IonTest() : ionfd(-1), ion_heaps() {}
+IonTest::IonTest() : m_ionFd(-1)
+{
+}
 
 void IonTest::SetUp() {
-    ionfd = ion_open();
-    ASSERT_GE(ionfd, 0);
-
-    int heap_count;
-    int ret = ion_query_heap_cnt(ionfd, &heap_count);
-    ASSERT_EQ(ret, 0);
-    ASSERT_GT(heap_count, 0);
-
-    ion_heaps.resize(heap_count, {});
-    ret = ion_query_get_heaps(ionfd, heap_count, ion_heaps.data());
-    ASSERT_EQ(ret, 0);
+    m_ionFd = ion_open();
+    ASSERT_GE(m_ionFd, 0);
 }
 
 void IonTest::TearDown() {
-    ion_close(ionfd);
+    ion_close(m_ionFd);
+}
+
+IonAllHeapsTest::IonAllHeapsTest() :
+        m_firstHeap(0),
+        m_lastHeap(0),
+        m_allHeaps()
+{
+}
+
+void IonAllHeapsTest::SetUp() {
+    int fd = ion_open();
+    ASSERT_GE(fd, 0);
+
+    for (int i = 1; i != 0; i <<= 1) {
+        ion_user_handle_t handle = 0;
+        int ret;
+        ret = ion_alloc(fd, 4096, 0, i, 0, &handle);
+        if (ret == 0 && handle != 0) {
+            ion_free(fd, handle);
+            if (!m_firstHeap) {
+                m_firstHeap = i;
+            }
+            m_lastHeap = i;
+            m_allHeaps.push_back(i);
+        } else {
+            ASSERT_EQ(-ENODEV, ret);
+        }
+    }
+    ion_close(fd);
+
+    EXPECT_NE(0U, m_firstHeap);
+    EXPECT_NE(0U, m_lastHeap);
+
+    RecordProperty("Heaps", m_allHeaps.size());
+    IonTest::SetUp();
+}
+
+void IonAllHeapsTest::TearDown() {
+    IonTest::TearDown();
 }
diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
index c78fe41..4098214 100644
--- a/libion/tests/ion_test_fixture.h
+++ b/libion/tests/ion_test_fixture.h
@@ -18,19 +18,29 @@
 #define ION_TEST_FIXTURE_H_
 
 #include <gtest/gtest.h>
-#include <linux/ion_4.12.h>
-#include <vector>
 
 using ::testing::Test;
 
 class IonTest : public virtual Test {
-  public:
+ public:
     IonTest();
-    virtual ~IonTest(){};
+	virtual ~IonTest() {};
+	virtual void SetUp();
+	virtual void TearDown();
+	int m_ionFd;
+};
+
+class IonAllHeapsTest : public IonTest {
+ public:
+    IonAllHeapsTest();
+    virtual ~IonAllHeapsTest() {};
     virtual void SetUp();
     virtual void TearDown();
-    int ionfd;
-    std::vector<struct ion_heap_data> ion_heaps;
+
+    unsigned int m_firstHeap;
+    unsigned int m_lastHeap;
+
+    std::vector<unsigned int> m_allHeaps;
 };
 
 #endif /* ION_TEST_FIXTURE_H_ */
diff --git a/libion/tests/map_test.cpp b/libion/tests/map_test.cpp
index f1b47b7..c006dc8 100644
--- a/libion/tests/map_test.cpp
+++ b/libion/tests/map_test.cpp
@@ -15,30 +15,61 @@
  */
 
 #include <sys/mman.h>
-#include <unistd.h>
 
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
+
 #include "ion_test_fixture.h"
 
-class Map : public IonTest {};
+class Map : public IonAllHeapsTest {
+};
 
-TEST_F(Map, MapFd) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Map, MapHandle)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            ion_user_handle_t handle = 0;
+
+            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
+            ASSERT_TRUE(handle != 0);
+
+            int map_fd = -1;
+            unsigned char *ptr = NULL;
+            ASSERT_EQ(0, ion_map(m_ionFd, handle, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0, &ptr, &map_fd));
+            ASSERT_TRUE(ptr != NULL);
+            ASSERT_GE(map_fd, 0);
+
+            ASSERT_EQ(0, close(map_fd));
+
+            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+
+            memset(ptr, 0xaa, size);
+
+            ASSERT_EQ(0, munmap(ptr, size));
+        }
+    }
+}
+
+TEST_F(Map, MapFd)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
 
-            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void* ptr;
+            void *ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
+
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
@@ -48,51 +79,53 @@
     }
 }
 
-TEST_F(Map, MapOffset) {
-    for (const auto& heap : ion_heaps) {
-        SCOPED_TRACE(::testing::Message()
-                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+TEST_F(Map, MapOffset)
+{
+    for (unsigned int heapMask : m_allHeaps) {
+        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
         int map_fd = -1;
 
-        ASSERT_EQ(0, ion_alloc_fd(ionfd, getpagesize() * 2, 0, (1 << heap.heap_id), 0, &map_fd));
+        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, PAGE_SIZE * 2, 0, heapMask, 0, &map_fd));
         ASSERT_GE(map_fd, 0);
 
-        unsigned char* ptr;
-        ptr = (unsigned char*)mmap(NULL, getpagesize() * 2, PROT_READ | PROT_WRITE, MAP_SHARED,
-                                   map_fd, 0);
+        unsigned char *ptr;
+        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
         ASSERT_TRUE(ptr != NULL);
 
-        memset(ptr, 0, getpagesize());
-        memset(ptr + getpagesize(), 0xaa, getpagesize());
+        memset(ptr, 0, PAGE_SIZE);
+        memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE);
 
-        ASSERT_EQ(0, munmap(ptr, getpagesize() * 2));
+        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE * 2));
 
-        ptr = (unsigned char*)mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, map_fd,
-                                   getpagesize());
+        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, PAGE_SIZE);
         ASSERT_TRUE(ptr != NULL);
+
         ASSERT_EQ(ptr[0], 0xaa);
-        ASSERT_EQ(ptr[getpagesize() - 1], 0xaa);
-        ASSERT_EQ(0, munmap(ptr, getpagesize()));
+        ASSERT_EQ(ptr[PAGE_SIZE - 1], 0xaa);
+
+        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE));
+
         ASSERT_EQ(0, close(map_fd));
     }
 }
 
-TEST_F(Map, MapCached) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Map, MapCached)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
             unsigned int flags = ION_FLAG_CACHED;
 
-            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), flags, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void* ptr;
+            void *ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
+
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
@@ -102,22 +135,23 @@
     }
 }
 
-TEST_F(Map, MapCachedNeedsSync) {
-    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
-    for (const auto& heap : ion_heaps) {
+TEST_F(Map, MapCachedNeedsSync)
+{
+    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
+    for (unsigned int heapMask : m_allHeaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message()
-                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
             unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
 
-            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), flags, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void* ptr;
+            void *ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
+
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
diff --git a/libion/tests/modular_heap_check.cpp b/libion/tests/modular_heap_check.cpp
deleted file mode 100644
index 5505c5a..0000000
--- a/libion/tests/modular_heap_check.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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 <ion/ion.h>
-#include "ion_test_fixture.h"
-
-class ModularHeapCheck : public IonTest {};
-
-TEST_F(ModularHeapCheck, ModularHeapCheckSimple) {
-    if (ion_is_using_modular_heaps(ionfd)) {
-        std::cout << "Heaps are modular." << std::endl;
-    } else {
-        std::cout << "Heaps are built-in." << std::endl;
-    }
-}
diff --git a/libion/tests/system_heap.cpp b/libion/tests/system_heap.cpp
deleted file mode 100644
index fb63888..0000000
--- a/libion/tests/system_heap.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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 <gtest/gtest.h>
-#include <iostream>
-
-#include <ion/ion.h>
-#include "ion_test_fixture.h"
-
-class SystemHeap : public IonTest {};
-
-TEST_F(SystemHeap, Presence) {
-    bool system_heap_found = false;
-    for (const auto& heap : ion_heaps) {
-        if (heap.type == ION_HEAP_TYPE_SYSTEM) {
-            system_heap_found = true;
-            EXPECT_TRUE((1 << heap.heap_id) & ION_HEAP_SYSTEM_MASK);
-        }
-    }
-    // We now expect the system heap to exist from Android
-    ASSERT_TRUE(system_heap_found);
-}
-
-TEST_F(SystemHeap, Allocate) {
-    int fd;
-    ASSERT_EQ(0, ion_alloc_fd(ionfd, getpagesize(), 0, ION_HEAP_SYSTEM_MASK, 0, &fd));
-    ASSERT_TRUE(fd != 0);
-    ASSERT_EQ(close(fd), 0);  // free the buffer
-}
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index b388e95..dda491a 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -16,3 +16,17 @@
     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/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/Android.bp b/libkeyutils/mini_keyctl/Android.bp
deleted file mode 100644
index a04a3db..0000000
--- a/libkeyutils/mini_keyctl/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-cc_library_static {
-    name: "libmini_keyctl_static",
-    srcs: [
-        "mini_keyctl_utils.cpp"
-    ],
-    shared_libs: [
-        "libbase",
-        "libkeyutils",
-    ],
-    cflags: ["-Werror", "-Wall", "-Wextra"],
-    export_include_dirs: ["."],
-}
-
-cc_binary {
-    name: "mini-keyctl",
-    srcs: [
-        "mini_keyctl.cpp",
-    ],
-    static_libs: [
-        "libmini_keyctl_static",
-    ],
-    shared_libs: [
-        "libbase",
-        "libkeyutils",
-    ],
-    cflags: ["-Werror", "-Wall", "-Wextra"],
-}
diff --git a/libkeyutils/mini_keyctl/mini_keyctl.cpp b/libkeyutils/mini_keyctl/mini_keyctl.cpp
deleted file mode 100644
index 8aace9a..0000000
--- a/libkeyutils/mini_keyctl/mini_keyctl.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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 <dirent.h>
-#include <errno.h>
-#include <error.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <iterator>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/parseint.h>
-#include <keyutils.h>
-#include <mini_keyctl_utils.h>
-
-constexpr int kMaxCertSize = 4096;
-
-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 Unlink(key_serial_t key, const std::string& keyring) {
-  key_serial_t keyring_id = android::GetKeyringId(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 = android::GetKeyringId(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 = android::GetKeyringId(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 = android::GetKeyringId(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;
-}
-
-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/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp
deleted file mode 100644
index fb9503f..0000000
--- a/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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 <fstream>
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-
-namespace android {
-
-namespace {
-
-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>{}};
-}
-
-}  // namespace
-
-// Find the keyring id. request_key(2) only finds keys in the process, session or thread keyring
-// hierarchy, but not internal keyring of a kernel subsystem (e.g. .fs-verity). To support all
-// cases, this function looks up a keyring's ID by parsing /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.
-key_serial_t GetKeyringId(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()) {
-    PLOG(ERROR) << "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)) {
-      LOG(ERROR) << "Unexpected key format in /proc/keys: " << key_id;
-      return -1;
-    }
-    return keyring_id;
-  }
-  return -1;
-}
-
-}  // namespace android
diff --git a/libkeyutils/mini_keyctl/mini_keyctl_utils.h b/libkeyutils/mini_keyctl/mini_keyctl_utils.h
deleted file mode 100644
index cc31d29..0000000
--- a/libkeyutils/mini_keyctl/mini_keyctl_utils.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_
-#define _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_
-
-#include <string>
-
-#include <keyutils.h>
-
-namespace android {
-key_serial_t GetKeyringId(const std::string& keyring_desc);
-}  // namespace android
-
-#endif  // _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
new file mode 100644
index 0000000..56afea4
--- /dev/null
+++ b/libkeyutils/mini_keyctl_utils.cpp
@@ -0,0 +1,184 @@
+/*
+ * 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 <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/logging.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 bool GetKeyringId(const std::string& keyring_desc, key_serial_t* keyring_id) {
+  if (!keyring_id) {
+    LOG(ERROR) << "keyring_id is null";
+    return false;
+  }
+
+  // If the keyring id is already a hex number, directly convert it to keyring id
+  if (android::base::ParseInt(keyring_desc.c_str(), keyring_id)) {
+    return true;
+  }
+
+  // Only keys allowed by SELinux rules will be shown here.
+  std::ifstream proc_keys_file("/proc/keys");
+  if (!proc_keys_file.is_open()) {
+    PLOG(ERROR) << "Failed to open /proc/keys";
+    return false;
+  }
+
+  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 = 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;
+    }
+    *keyring_id = std::stoi(key_id, nullptr, 16);
+    return true;
+  }
+  return false;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can't find keyring " << keyring;
+    return 1;
+  }
+
+  if (keyctl_unlink(key, keyring_id) < 0) {
+    PLOG(ERROR) << "Failed to unlink key 0x" << std::hex << key << " from keyring " << keyring_id;
+    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) {
+    LOG(ERROR) << "Certificate too large";
+    return 1;
+  }
+
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can not find keyring id";
+    return 1;
+  }
+
+  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+  if (key < 0) {
+    PLOG(ERROR) << "Failed to add key";
+    return 1;
+  }
+
+  LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+  return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Can not find keyring id";
+    return 1;
+  }
+
+  // read from stdin to get the certificates
+  std::istreambuf_iterator<char> begin(std::cin), end;
+  std::string data(begin, end);
+
+  if (data.size() > kMaxCertSize) {
+    LOG(ERROR) << "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) {
+    PLOG(ERROR) << "Failed to add key";
+    return 1;
+  }
+
+  LOG(INFO) << "Key " << desc << " added to " << keyring << " with key id: 0x" << std::hex << key;
+  return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+  key_serial_t keyring_id;
+  if (!GetKeyringId(keyring, &keyring_id)) {
+    LOG(ERROR) << "Cannot find keyring id";
+    return 1;
+  }
+
+  if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+    PLOG(ERROR) << "Cannot restrict keyring " << keyring;
+    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) {
+    PLOG(ERROR) << "Cannot get security context of key 0x" << std::hex << key;
+    return std::string();
+  }
+  if (retval > kMaxSupportedSize) {
+    LOG(ERROR) << "The key has unexpectedly long security context than " << 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/Android.bp b/liblog/Android.bp
index 91bd52c..da475cb 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,6 +15,8 @@
 //
 
 liblog_sources = [
+    "config_read.cpp",
+    "config_write.cpp",
     "log_event_list.cpp",
     "log_event_write.cpp",
     "logger_lock.cpp",
@@ -22,6 +24,7 @@
     "logger_read.cpp",
     "logger_write.cpp",
     "logprint.cpp",
+    "stderr_write.cpp",
 ]
 liblog_host_sources = [
     "fake_log_device.cpp",
@@ -42,7 +45,6 @@
     host_supported: true,
     vendor_available: true,
     recovery_available: true,
-    native_bridge_supported: true,
     export_include_dirs: ["include"],
     system_shared_libs: [],
     stl: "none",
@@ -65,7 +67,6 @@
     name: "liblog",
     host_supported: true,
     recovery_available: true,
-    native_bridge_supported: true,
     srcs: liblog_sources,
 
     target: {
@@ -105,15 +106,8 @@
         versions: ["10000"],
     },
 
-    // TODO(tomcherry): Renable this before release branch is cut
-    header_abi_checker: {
-        enabled: false,
-    },
-
     cflags: [
-        "-Wall",
         "-Werror",
-        "-Wextra",
         // This is what we want to do:
         //  liblog_cflags := $(shell \
         //   sed -n \
@@ -144,7 +138,6 @@
 
 llndk_library {
     name: "liblog",
-    native_bridge_supported: true,
     symbol_file: "liblog.map.txt",
     export_include_dirs: ["include_vndk"],
 }
diff --git a/liblog/README.md b/liblog/README.md
index 871399a..98bee9f 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -96,6 +96,11 @@
 
     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
 -----------
 
@@ -139,6 +144,11 @@
 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
 ------
 
diff --git a/liblog/README.protocol.md b/liblog/README.protocol.md
deleted file mode 100644
index fef29c9..0000000
--- a/liblog/README.protocol.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# liblog -> logd
-
-The data that liblog sends to logd is represented below.
-
-    struct {
-        android_log_header_t header;
-        union {
-           struct {
-                char     prio;
-                char     tag[...];
-                char     message[...];
-            } string;
-            struct {
-                android_event_header_t event_header;
-                android_event_*_t      payload[...];
-            } binary;
-        };
-    };
-
-The payload, excluding the header, has a max size of LOGGER_ENTRY_MAX_PAYLOAD.
-
-## header
-
-The header is added immediately before sending the log message to logd.
-
-## `string` payload
-
-The `string` part of the union is for normal buffers (main, system, radio, etc) and consists of a
-single character priority, followed by a variable length null terminated string for the tag, and
-finally a variable length null terminated string for the message.
-
-This payload is used for the `__android_log_buf_write()` family of functions.
-
-## `binary` payload
-
-The `binary` part of the union is for binary buffers (events, security, etc) and consists of an
-android_event_header_t struct followed by a variable number of android_event_*_t
-(android_event_list_t, android_event_int_t, etc) structs.
-
-If multiple android_event_*_t elements are present, then they must be in a list and the first
-element in payload must be an android_event_list_t.
-
-This payload is used for the `__android_log_bwrite()` family of functions. It is additionally used
-for `android_log_write_list()` and the related functions that manipulate event lists.
-
-# logd -> liblog
-
-logd sends a `logger_entry` struct to liblog followed by the payload. The payload is identical to
-the payloads defined above. The max size of the entire message from logd is LOGGER_ENTRY_MAX_LEN.
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
new file mode 100644
index 0000000..212b8a0
--- /dev/null
+++ b/liblog/config_read.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <cutils/list.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+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,                            \
+                               struct android_log_transport_read, node);      \
+       ((transp) != node_to_item((transports),                                \
+                                 struct android_log_transport_read, node)) && \
+       ((transp) != node_to_item((transp)->node.next,                         \
+                                 struct android_log_transport_read, node));   \
+       (transp) = node_to_item((transp)->node.next,                           \
+                               struct android_log_transport_read, node))
+
+#define read_transport_for_each_safe(transp, n, transports)                   \
+  for ((transp) = node_to_item((transports)->next,                            \
+                               struct android_log_transport_read, node),      \
+      (n) = (transp)->node.next;                                              \
+       ((transp) != node_to_item((transports),                                \
+                                 struct android_log_transport_read, node)) && \
+       ((transp) !=                                                           \
+        node_to_item((n), struct android_log_transport_read, node));          \
+       (transp) = node_to_item((n), struct android_log_transport_read, node), \
+      (n) = (transp)->node.next)
+
+void __android_log_config_read();
+void __android_log_config_read_close();
+
+__END_DECLS
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
new file mode 100644
index 0000000..a901f13
--- /dev/null
+++ b/liblog/config_write.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <cutils/list.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+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,                             \
+                               struct android_log_transport_write, node);      \
+       ((transp) != node_to_item((transports),                                 \
+                                 struct android_log_transport_write, node)) && \
+       ((transp) != node_to_item((transp)->node.next,                          \
+                                 struct android_log_transport_write, node));   \
+       (transp) = node_to_item((transp)->node.next,                            \
+                               struct android_log_transport_write, node))
+
+#define write_transport_for_each_safe(transp, n, transports)                   \
+  for ((transp) = node_to_item((transports)->next,                             \
+                               struct android_log_transport_write, node),      \
+      (n) = (transp)->node.next;                                               \
+       ((transp) != node_to_item((transports),                                 \
+                                 struct android_log_transport_write, node)) && \
+       ((transp) !=                                                            \
+        node_to_item((n), struct android_log_transport_write, node));          \
+       (transp) = node_to_item((n), struct android_log_transport_write, node), \
+      (n) = (transp)->node.next)
+
+void __android_log_config_write();
+void __android_log_config_write_close();
+
+__END_DECLS
diff --git a/liblog/fake_writer.cpp b/liblog/fake_writer.cpp
index f1ddff1..c0b0e69 100644
--- a/liblog/fake_writer.cpp
+++ b/liblog/fake_writer.cpp
@@ -20,11 +20,11 @@
 
 #include <log/log.h>
 
+#include "config_write.h"
 #include "fake_log_device.h"
 #include "log_portability.h"
 #include "logger.h"
 
-static int fakeAvailable(log_id_t);
 static int fakeOpen();
 static void fakeClose();
 static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
@@ -32,19 +32,15 @@
 static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
 
 struct android_log_transport_write fakeLoggerWrite = {
-    .name = "fake",
-    .logMask = 0,
+    .node = {&fakeLoggerWrite.node, &fakeLoggerWrite.node},
     .context.priv = &logFds,
-    .available = fakeAvailable,
+    .name = "fake",
+    .available = NULL,
     .open = fakeOpen,
     .close = fakeClose,
     .write = fakeWrite,
 };
 
-static int fakeAvailable(log_id_t) {
-  return 0;
-}
-
 static int fakeOpen() {
   int i;
 
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index 7290789..b2f0ed9 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -96,14 +96,20 @@
  * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
  */
 int __android_log_print(int prio, const char* tag, const char* fmt, ...)
-    __attribute__((__format__(printf, 3, 4)));
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 3, 4)))
+#endif
+    ;
 
 /**
  * Equivalent to `__android_log_print`, but taking a `va_list`.
  * (If `__android_log_print` is like `printf`, this is like `vprintf`.)
  */
 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
-    __attribute__((__format__(printf, 3, 0)));
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 3, 0)))
+#endif
+    ;
 
 /**
  * Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
@@ -116,14 +122,20 @@
  *
  * Most callers should use
  * [assert(3)](http://man7.org/linux/man-pages/man3/assert.3.html) from
- * `&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.
+ * `<assert.h>` instead, or the `__assert` and `__assert2` functions provided by
+ * bionic if more control is needed. They support automatically including the
+ * source filename and line number more conveniently than this function.
  */
-void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...)
-    __attribute__((__noreturn__)) __attribute__((__format__(printf, 3, 4)));
+void __android_log_assert(const char* cond, const char* tag, const char* fmt,
+                          ...)
+#if defined(__GNUC__)
+    __attribute__((__noreturn__))
+    __attribute__((__format__(printf, 3, 4)))
+#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().
@@ -150,6 +162,7 @@
 
   LOG_ID_MAX
 } log_id_t;
+#endif
 
 /**
  * Writes the constant string `text` to the log buffer `id`,
@@ -157,7 +170,8 @@
  *
  * Apps should use __android_log_write() instead.
  */
-int __android_log_buf_write(int bufID, int prio, const char* tag, const char* text);
+int __android_log_buf_write(int bufID, int prio, const char* tag,
+                            const char* text);
 
 /**
  * Writes a formatted string to log buffer `id`,
@@ -167,8 +181,12 @@
  *
  * Apps should use __android_log_print() instead.
  */
-int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
-    __attribute__((__format__(printf, 4, 5)));
+int __android_log_buf_print(int bufID, int prio, const char* tag,
+                            const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 4, 5)))
+#endif
+    ;
 
 #ifdef __cplusplus
 }
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index f7ec208..2687b3a 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <stddef.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 90d1e76..5928649 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -22,6 +22,7 @@
 #endif
 #include <stdint.h> /* uint16_t, int32_t */
 #include <stdio.h>
+#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -64,6 +65,21 @@
 #endif
 #endif
 
+/* --------------------------------------------------------------------- */
+
+/*
+ * This file uses ", ## __VA_ARGS__" zero-argument token pasting to
+ * work around issues with debug-only syntax errors in assertions
+ * that are missing format strings.  See commit
+ * 19299904343daf191267564fe32e6cd5c165cd42
+ */
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+/* --------------------------------------------------------------------- */
+
 /*
  * Event logging.
  */
@@ -148,6 +164,10 @@
  */
 void __android_log_close(void);
 
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/liblog/include/log/log_event_list.h b/liblog/include/log/log_event_list.h
index deadf20..636d417 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -36,11 +36,16 @@
 /*
  * The opaque context used to manipulate lists of events.
  */
+#ifndef __android_log_context_defined
+#define __android_log_context_defined
 typedef struct android_log_context_internal* android_log_context;
+#endif
 
 /*
  * Elements returned when reading a list of events.
  */
+#ifndef __android_log_list_element_defined
+#define __android_log_list_element_defined
 typedef struct {
   AndroidEventLogType type;
   uint16_t complete;
@@ -52,6 +57,7 @@
     float float32;
   } data;
 } android_log_list_element;
+#endif
 
 /*
  * Creates a context associated with an event tag to write elements to
@@ -98,6 +104,8 @@
 int android_log_destroy(android_log_context* ctx);
 
 #ifdef __cplusplus
+#ifndef __class_android_log_event_list_defined
+#define __class_android_log_event_list_defined
 /* android_log_list C++ helpers */
 extern "C++" {
 class android_log_event_list {
@@ -272,6 +280,7 @@
 };
 }
 #endif
+#endif
 
 #ifdef __cplusplus
 }
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c8fafe7..c052a50 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -16,19 +16,41 @@
 
 #pragma once
 
-#include <android/log.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#ifndef log_id_t_defined
+#define log_id_t_defined
+typedef enum log_id {
+  LOG_ID_MIN = 0,
+
+  LOG_ID_MAIN = 0,
+  LOG_ID_RADIO = 1,
+  LOG_ID_EVENTS = 2,
+  LOG_ID_SYSTEM = 3,
+  LOG_ID_CRASH = 4,
+  LOG_ID_STATS = 5,
+  LOG_ID_SECURITY = 6,
+  LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
+
+  LOG_ID_MAX
+} log_id_t;
+#endif
+#define sizeof_log_id_t sizeof(typeof_log_id_t)
+#define typeof_log_id_t unsigned char
+
 /*
  * Send a simple string to the log.
  */
 int __android_log_buf_write(int bufID, int prio, const char* tag,
                             const char* text);
-int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
-    __attribute__((__format__(printf, 4, 5)));
+int __android_log_buf_print(int bufID, int prio, const char* tag,
+                            const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 4, 5)))
+#endif
+    ;
 
 /*
  * log_id_t helpers
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index ee3b250..fdef306 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -50,9 +50,64 @@
 
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wzero-length-array"
+/*
+ * The userspace structure for version 1 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_defined
+#define __struct_logger_entry_defined
 struct logger_entry {
+  uint16_t len;   /* length of the payload */
+  uint16_t __pad; /* no matter what, we get 2 bytes of padding */
+  int32_t pid;    /* generating process's pid */
+  int32_t tid;    /* generating process's tid */
+  int32_t sec;    /* seconds since Epoch */
+  int32_t nsec;   /* nanoseconds */
+  char msg[0]; /* the entry's payload */
+};
+#endif
+
+/*
+ * The userspace structure for version 2 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v2_defined
+#define __struct_logger_entry_v2_defined
+struct logger_entry_v2 {
   uint16_t len;      /* length of the payload */
-  uint16_t hdr_size; /* sizeof(struct logger_entry) */
+  uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */
+  int32_t pid;       /* generating process's pid */
+  int32_t tid;       /* generating process's tid */
+  int32_t sec;       /* seconds since Epoch */
+  int32_t nsec;      /* nanoseconds */
+  uint32_t euid;     /* effective UID of logger */
+  char msg[0]; /* the entry's payload */
+} __attribute__((__packed__));
+#endif
+
+/*
+ * The userspace structure for version 3 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v3_defined
+#define __struct_logger_entry_v3_defined
+struct logger_entry_v3 {
+  uint16_t len;      /* length of the payload */
+  uint16_t hdr_size; /* sizeof(struct logger_entry_v3) */
+  int32_t pid;       /* generating process's pid */
+  int32_t tid;       /* generating process's tid */
+  int32_t sec;       /* seconds since Epoch */
+  int32_t nsec;      /* nanoseconds */
+  uint32_t lid;      /* log id of the payload */
+  char msg[0]; /* the entry's payload */
+} __attribute__((__packed__));
+#endif
+
+/*
+ * The userspace structure for version 4 of the logger_entry ABI.
+ */
+#ifndef __struct_logger_entry_v4_defined
+#define __struct_logger_entry_v4_defined
+struct logger_entry_v4 {
+  uint16_t len;      /* length of the payload */
+  uint16_t hdr_size; /* sizeof(struct logger_entry_v4) */
   int32_t pid;       /* generating process's pid */
   uint32_t tid;      /* generating process's tid */
   uint32_t sec;      /* seconds since Epoch */
@@ -61,6 +116,7 @@
   uint32_t uid;      /* generating process's uid */
   char msg[0]; /* the entry's payload */
 };
+#endif
 #pragma clang diagnostic pop
 
 /*
@@ -77,10 +133,16 @@
  */
 #define LOGGER_ENTRY_MAX_LEN (5 * 1024)
 
+#ifndef __struct_log_msg_defined
+#define __struct_log_msg_defined
 struct log_msg {
   union {
     unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1];
-    struct logger_entry entry;
+    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;
   } __attribute__((aligned(4)));
 #ifdef __cplusplus
   /* Matching log_time operators */
@@ -114,14 +176,22 @@
   }
   char* msg() {
     unsigned short hdr_size = entry.hdr_size;
-    if (hdr_size != sizeof(entry)) {
+    if (!hdr_size) {
+      hdr_size = sizeof(entry_v1);
+    }
+    if ((hdr_size < sizeof(entry_v1)) || (hdr_size > sizeof(entry))) {
       return nullptr;
     }
     return reinterpret_cast<char*>(buf) + hdr_size;
   }
-  unsigned int len() { return entry.hdr_size + entry.len; }
+  unsigned int len() {
+    return (entry.hdr_size ? entry.hdr_size
+                           : static_cast<uint16_t>(sizeof(entry_v1))) +
+           entry.len;
+  }
 #endif
 };
+#endif
 
 struct logger;
 
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 6b4458c..09c9910 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -24,6 +24,9 @@
 #define US_PER_SEC 1000000ULL
 #define MS_PER_SEC 1000ULL
 
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
 #define LOG_TIME_SEC(t) ((t)->tv_sec)
 /* next power of two after NS_PER_SEC */
 #define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
@@ -32,6 +35,11 @@
 
 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
+ * efficient behavior. Also, pass-by-reference breaks C/C++ ABI.
+ */
 struct log_time {
  public:
   uint32_t tv_sec = 0; /* good to Feb 5 2106 */
@@ -161,3 +169,5 @@
 } __attribute__((__packed__)) log_time;
 
 #endif /* __cplusplus */
+
+#endif /* __struct_log_time_defined */
diff --git a/liblog/include/log/log_transport.h b/liblog/include/log/log_transport.h
new file mode 100644
index 0000000..b48761a
--- /dev/null
+++ b/liblog/include/log/log_transport.h
@@ -0,0 +1,34 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed.  It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Logging transports, bit mask to select features. Function returns selection.
+ */
+/* clang-format off */
+#define LOGGER_DEFAULT 0x00
+#define LOGGER_LOGD    0x01
+#define LOGGER_KERNEL  0x02 /* Reserved/Deprecated */
+#define LOGGER_NULL    0x04 /* Does not release resources of other selections */
+#define LOGGER_RESERVED 0x08 /* Reserved, previously for logging to local memory */
+#define LOGGER_STDERR  0x10 /* logs sent to stderr */
+/* clang-format on */
+
+/* Both return the selected transport flag mask, or negative errno */
+int android_set_log_transport(int transport_flag);
+int android_get_log_transport();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/liblog/include/log/logprint.h b/liblog/include/log/logprint.h
index 7dfd914..8f4b187 100644
--- a/liblog/include/log/logprint.h
+++ b/liblog/include/log/logprint.h
@@ -16,8 +16,7 @@
 
 #pragma once
 
-#include <stdint.h>
-#include <sys/types.h>
+#include <pthread.h>
 
 #include <android/log.h>
 #include <log/event_tag_map.h>
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index d3b72bc..5e04148 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -47,7 +47,7 @@
 
 /* Header Structure to logd, and second header for pstore */
 typedef struct __attribute__((__packed__)) {
-  uint8_t id;
+  typeof_log_id_t id;
   uint16_t tid;
   log_time realtime;
 } android_log_header_t;
@@ -57,18 +57,6 @@
   int32_t tag;  // Little Endian Order
 } android_event_header_t;
 
-// Event payload EVENT_TYPE_LIST
-typedef struct __attribute__((__packed__)) {
-  int8_t type;  // EVENT_TYPE_LIST
-  int8_t element_count;
-} android_event_list_t;
-
-// Event payload EVENT_TYPE_FLOAT
-typedef struct __attribute__((__packed__)) {
-  int8_t type;  // EVENT_TYPE_FLOAT
-  float data;
-} android_event_float_t;
-
 /* Event payload EVENT_TYPE_INT */
 typedef struct __attribute__((__packed__)) {
   int8_t type;   // EVENT_TYPE_INT
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index aea3350..ce4c53c 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -51,7 +51,7 @@
 LIBLOG_O {
   global:
     __android_log_is_loggable_len;
-    __android_log_is_debuggable; # apex vndk
+    __android_log_is_debuggable; # vndk
 };
 
 LIBLOG_Q {
@@ -61,15 +61,15 @@
     __android_log_bwrite; # apex
     __android_log_close; # apex
     __android_log_security; # apex
-    __android_log_security_bswrite; # apex
-    android_log_reset; # vndk
-    android_log_parser_reset; # vndk
+    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;
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index 7882c96..b1b527c 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -51,9 +51,11 @@
 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;
-  size_t needed = sizeof(android_event_list_t);
+  needed = sizeof(uint8_t) + sizeof(uint8_t);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
   }
@@ -86,6 +88,7 @@
 
 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)));
@@ -141,6 +144,7 @@
 }
 
 int android_log_write_list_begin(android_log_context ctx) {
+  size_t needed;
   android_log_context_internal* context;
 
   context = (android_log_context_internal*)ctx;
@@ -151,7 +155,7 @@
     context->overflow = true;
     return -EOVERFLOW;
   }
-  size_t needed = sizeof(android_event_list_t);
+  needed = sizeof(uint8_t) + sizeof(uint8_t);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
     return -EIO;
@@ -165,59 +169,84 @@
   if (context->overflow) {
     return -EIO;
   }
-  auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[context->pos]);
-  event_list->type = EVENT_TYPE_LIST;
-  event_list->element_count = 0;
+  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) {
-  android_log_context_internal* context = (android_log_context_internal*)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->overflow) {
     return -EIO;
   }
-  size_t needed = sizeof(android_event_int_t);
+  needed = sizeof(uint8_t) + sizeof(value);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
     return -EIO;
   }
   context->count[context->list_nest_depth]++;
-  auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[context->pos]);
-  event_int->type = EVENT_TYPE_INT;
-  event_int->data = value;
+  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) {
-  android_log_context_internal* context = (android_log_context_internal*)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->overflow) {
     return -EIO;
   }
-  size_t needed = sizeof(android_event_long_t);
+  needed = sizeof(uint8_t) + sizeof(value);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
     return -EIO;
   }
   context->count[context->list_nest_depth]++;
-  auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[context->pos]);
-  event_long->type = EVENT_TYPE_LONG;
-  event_long->data = value;
+  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) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+  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;
   }
@@ -227,8 +256,8 @@
   if (!value) {
     value = "";
   }
-  int32_t len = strnlen(value, maxlen);
-  size_t needed = sizeof(android_event_string_t) + len;
+  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);
@@ -238,11 +267,10 @@
     }
   }
   context->count[context->list_nest_depth]++;
-  auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[context->pos]);
-  event_string->type = EVENT_TYPE_STRING;
-  event_string->length = len;
+  context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+  copy4LE(&context->storage[context->pos + 1], len);
   if (len) {
-    memcpy(&event_string->data, value, len);
+    memcpy(&context->storage[context->pos + 5], value, len);
   }
   context->pos += needed;
   return len;
@@ -253,22 +281,26 @@
 }
 
 int android_log_write_float32(android_log_context ctx, float value) {
-  android_log_context_internal* context = (android_log_context_internal*)ctx;
+  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;
   }
-  size_t needed = sizeof(android_event_float_t);
+  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]++;
-  auto* event_float = reinterpret_cast<android_event_float_t*>(&context->storage[context->pos]);
-  event_float->type = EVENT_TYPE_FLOAT;
-  event_float->data = value;
+  context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
+  copy4LE(&context->storage[context->pos + 1], ivalue);
   context->pos += needed;
   return 0;
 }
@@ -369,6 +401,22 @@
 }
 
 /*
+ * 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
@@ -430,78 +478,18 @@
     return elem;
   }
 
-  elem.type = static_cast<AndroidEventLogType>(context->storage[pos]);
+  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: {
+    case EVENT_TYPE_INT:
       elem.len = sizeof(int32_t);
-      if ((pos + sizeof(android_event_int_t)) > context->len) {
-        elem.type = EVENT_TYPE_UNKNOWN;
-        return elem;
-      }
-
-      auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[pos]);
-      pos += sizeof(android_event_int_t);
-      elem.data.int32 = event_int->data;
-      /* common tangeable object suffix */
-      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 + sizeof(android_event_long_t)) > context->len) {
-        elem.type = EVENT_TYPE_UNKNOWN;
-        return elem;
-      }
-
-      auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[pos]);
-      pos += sizeof(android_event_long_t);
-      elem.data.int64 = event_long->data;
-      /* common tangeable object suffix */
-      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(android_event_string_t)) > context->len) {
-        elem.type = EVENT_TYPE_UNKNOWN;
-        elem.complete = true;
-        return elem;
-      }
-      auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[pos]);
-      pos += sizeof(android_event_string_t);
-      // Wire format is int32_t, but elem.len is uint16_t...
-      if (event_string->length >= UINT16_MAX) {
-        elem.type = EVENT_TYPE_UNKNOWN;
-        return elem;
-      }
-      elem.len = event_string->length;
       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.type = EVENT_TYPE_UNKNOWN;
+        return elem;
       }
-      elem.data.string = event_string->data;
+      elem.data.int32 = get4LE(&context->storage[pos]);
       /* common tangeable object suffix */
       pos += elem.len;
       elem.complete = !context->list_nest_depth && !context->count[0];
@@ -513,16 +501,61 @@
         context->pos = pos;
       }
       return elem;
-    }
 
-    case EVENT_TYPE_LIST: {
-      if ((pos + sizeof(android_event_list_t)) > context->len) {
+    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;
       }
-      auto* event_list = reinterpret_cast<android_event_list_t*>(&context->storage[pos]);
-      pos += sizeof(android_event_list_t);
+      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;
@@ -530,17 +563,15 @@
       if (context->count[context->list_nest_depth]) {
         context->count[context->list_nest_depth]--;
       }
-      context->list_stop = event_list->element_count == 0;
+      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] = event_list->element_count;
+        context->count[context->list_nest_depth] = context->storage[pos];
       }
-      context->pos = pos;
+      context->pos = pos + sizeof(uint8_t);
       return elem;
-    }
 
     case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
-      pos++;
       if (!peek) {
         context->pos = pos;
       }
diff --git a/liblog/log_portability.h b/liblog/log_portability.h
index b7279d1..468a498 100644
--- a/liblog/log_portability.h
+++ b/liblog/log_portability.h
@@ -19,6 +19,16 @@
 #include <sys/cdefs.h>
 #include <unistd.h>
 
+/*
+ * 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 extern "C" __attribute__((weak, visibility("default")))
+#endif
+
 /* possible missing definitions in sys/cdefs.h */
 
 /* DECLS */
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 619cf8c..b7ba782 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <endian.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -23,7 +24,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/param.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -35,10 +35,14 @@
 #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);
@@ -64,15 +68,16 @@
                             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,
-    .close = logdClose,
     .read = logdRead,
     .poll = logdPoll,
+    .close = logdClose,
     .clear = logdClear,
-    .setSize = logdSetSize,
     .getSize = logdGetSize,
+    .setSize = logdSetSize,
     .getReadableSize = logdGetReadableSize,
     .getPrune = logdGetPrune,
     .setPrune = logdSetPrune,
@@ -107,7 +112,7 @@
   strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
 
   int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
-  if (fd == -1) {
+  if (fd == 0) {
     return -1;
   }
 
@@ -276,13 +281,13 @@
   size_t n;
 
   n = snprintf(cp, remaining, "getStatistics");
-  n = MIN(n, remaining);
+  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);
+    n = min(n, remaining);
     remaining -= n;
     cp += n;
   }
@@ -316,11 +321,16 @@
   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 ret, remaining, sock;
+  int e, ret, remaining, sock;
 
   if (!logger_list) {
     return -EINVAL;
@@ -332,6 +342,12 @@
   }
 
   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;
@@ -348,7 +364,7 @@
   remaining = sizeof(buffer) - (cp - buffer);
   logger_for_each(logger, logger_list) {
     ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
-    ret = MIN(ret, remaining);
+    ret = min(ret, remaining);
     remaining -= ret;
     cp += ret;
     c = ',';
@@ -356,7 +372,7 @@
 
   if (logger_list->tail) {
     ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
-    ret = MIN(ret, remaining);
+    ret = min(ret, remaining);
     remaining -= ret;
     cp += ret;
   }
@@ -365,30 +381,46 @@
     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);
+      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);
+    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);
+    ret = min(ret, remaining);
     cp += ret;
   }
 
-  ret = TEMP_FAILURE_RETRY(write(sock, buffer, cp - buffer));
-  int write_errno = errno;
+  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) {
-      return -write_errno;
+    if ((ret == -1) && e) {
+      return -e;
     }
     if (ret == 0) {
       return -EIO;
@@ -406,21 +438,52 @@
 /* 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 = logdOpen(logger_list, transp);
+  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));
 
-  /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
-  ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0));
-  if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) {
-    return -EAGAIN;
+  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);
   }
 
-  if (ret == -1) {
-    return -errno;
+  /* 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;
 }
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index a22c3be..c3f72f4 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <endian.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -34,19 +35,23 @@
 #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 = {
-    .name = "logd",
-    .logMask = 0,
+    .node = {&logdLoggerWrite.node, &logdLoggerWrite.node},
     .context.sock = -EBADF,
+    .name = "logd",
     .available = logdAvailable,
     .open = logdOpen,
     .close = logdClose,
@@ -148,6 +153,24 @@
     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;
@@ -161,9 +184,9 @@
       android_log_event_int_t buffer;
 
       header.id = LOG_ID_SECURITY;
-      buffer.header.tag = LIBLOG_LOG_TAG;
+      buffer.header.tag = htole32(LIBLOG_LOG_TAG);
       buffer.payload.type = EVENT_TYPE_INT;
-      buffer.payload.data = snapshot;
+      buffer.payload.data = htole32(snapshot);
 
       newVec[headerLength].iov_base = &buffer;
       newVec[headerLength].iov_len = sizeof(buffer);
@@ -179,9 +202,9 @@
       android_log_event_int_t buffer;
 
       header.id = LOG_ID_EVENTS;
-      buffer.header.tag = LIBLOG_LOG_TAG;
+      buffer.header.tag = htole32(LIBLOG_LOG_TAG);
       buffer.payload.type = EVENT_TYPE_INT;
-      buffer.payload.data = snapshot;
+      buffer.payload.data = htole32(snapshot);
 
       newVec[headerLength].iov_base = &buffer;
       newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/liblog/logger.h b/liblog/logger.h
index 02cad22..1f632c0 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <stdatomic.h>
+#include <stdbool.h>
 
 #include <cutils/list.h>
 #include <log/log.h>
@@ -31,9 +32,12 @@
   void* priv;
   atomic_int sock;
   atomic_int fd;
+  struct listnode* node;
+  atomic_uintptr_t atomic_pointer;
 };
 
 struct android_log_transport_write {
+  struct listnode node;
   const char* name;                  /* human name to describe the transport */
   unsigned logMask;                  /* mask cache of available() success */
   union android_log_context_union context; /* Initialized by static allocation */
@@ -51,6 +55,7 @@
 struct android_log_logger;
 
 struct android_log_transport_read {
+  struct listnode node;
   const char* name; /* human name to describe the transport */
 
   /* Does not cause resources to be taken */
@@ -91,17 +96,9 @@
                       size_t len);
 };
 
-struct android_log_transport_context {
-  union android_log_context_union context; /* zero init per-transport context */
-
-  struct android_log_transport_read* transport;
-  unsigned logMask;      /* mask of requested log buffers */
-};
-
 struct android_log_logger_list {
   struct listnode logger;
-  android_log_transport_context transport_context;
-  bool transport_initialized;
+  struct listnode transport;
   int mode;
   unsigned int tail;
   log_time start;
@@ -115,7 +112,27 @@
   log_id_t logId;
 };
 
+struct android_log_transport_context {
+  struct listnode node;
+  union android_log_context_union context; /* zero init per-transport context */
+  struct android_log_logger_list* parent;
+
+  struct android_log_transport_read* transport;
+  unsigned logMask;      /* mask of requested log buffers */
+  int ret;               /* return value associated with following data */
+  struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
+};
+
 /* assumes caller has structures read-locked, single threaded, or fenced */
+#define transport_context_for_each(transp, logger_list)                          \
+  for ((transp) = node_to_item((logger_list)->transport.next,                    \
+                               struct android_log_transport_context, node);      \
+       ((transp) != node_to_item(&(logger_list)->transport,                      \
+                                 struct android_log_transport_context, node)) && \
+       ((transp)->parent == (logger_list));                                      \
+       (transp) = node_to_item((transp)->node.next,                              \
+                               struct android_log_transport_context, node))
+
 #define logger_for_each(logp, logger_list)                          \
   for ((logp) = node_to_item((logger_list)->logger.next,            \
                              struct android_log_logger, node);      \
@@ -143,4 +160,6 @@
 int __android_log_trylock();
 void __android_log_unlock();
 
+extern int __android_log_transport;
+
 __END_DECLS
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 4b4012a..4cf0846 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -29,6 +29,7 @@
 #include <cutils/list.h>
 #include <private/android_filesystem_config.h>
 
+#include "config_read.h"
 #include "log_portability.h"
 #include "logger.h"
 
@@ -54,6 +55,9 @@
 }
 
 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;
   }
@@ -62,62 +66,77 @@
     return -EINVAL;
   }
 
-  if (logger_list->transport_initialized) {
+  if (!list_empty(&logger_list->transport)) {
     return 0;
   }
 
-#if (FAKE_LOG_DEVICE == 0)
-  extern struct android_log_transport_read logdLoggerRead;
-  extern struct android_log_transport_read pmsgLoggerRead;
+  __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();
 
-  struct android_log_transport_read* transport;
-  transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
+  node = (logger_list->mode & ANDROID_LOG_PSTORE) ? &__android_log_persist_read
+                                                  : &__android_log_transport_read;
 
-  struct android_log_logger* logger;
-  unsigned logMask = 0;
+  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;
+    logger_for_each(logger, logger_list) {
+      log_id_t logId = logger->logId;
 
-    if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) {
+      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;
     }
-    if (transport->read && (!transport->available || transport->available(logId) >= 0)) {
-      logMask |= 1 << logId;
+    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 (!logMask) {
+  if (list_empty(&logger_list->transport)) {
     return -ENODEV;
   }
-
-  logger_list->transport_context.transport = transport;
-  logger_list->transport_context.logMask = logMask;
-#endif
   return 0;
 }
 
-#define LOGGER_FUNCTION(logger, def, func, args...)                                               \
-  ssize_t ret = -EINVAL;                                                                          \
-  android_log_logger* logger_internal = reinterpret_cast<android_log_logger*>(logger);            \
-                                                                                                  \
-  if (!logger_internal) {                                                                         \
-    return ret;                                                                                   \
-  }                                                                                               \
-  ret = init_transport_context(logger_internal->parent);                                          \
-  if (ret < 0) {                                                                                  \
-    return ret;                                                                                   \
-  }                                                                                               \
-                                                                                                  \
-  ret = (def);                                                                                    \
-  android_log_transport_context* transport_context = &logger_internal->parent->transport_context; \
-  if (transport_context->logMask & (1 << logger_internal->logId) &&                               \
-      transport_context->transport && transport_context->transport->func) {                       \
-    ssize_t retval =                                                                              \
-        (transport_context->transport->func)(logger_internal, transport_context, ##args);         \
-    if (ret >= 0 || ret == (def)) {                                                               \
-      ret = retval;                                                                               \
-    }                                                                                             \
-  }                                                                                               \
+#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) {
@@ -148,24 +167,25 @@
   LOGGER_FUNCTION(logger, 4, version);
 }
 
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...)                                  \
-  android_log_logger_list* logger_list_internal =                                              \
-      reinterpret_cast<android_log_logger_list*>(logger_list);                                 \
-                                                                                               \
-  ssize_t ret = init_transport_context(logger_list_internal);                                  \
-  if (ret < 0) {                                                                               \
-    return ret;                                                                                \
-  }                                                                                            \
-                                                                                               \
-  ret = (def);                                                                                 \
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context; \
-  if (transport_context->transport && transport_context->transport->func) {                    \
-    ssize_t retval =                                                                           \
-        (transport_context->transport->func)(logger_list_internal, transport_context, ##args); \
-    if (ret >= 0 || ret == (def)) {                                                            \
-      ret = retval;                                                                            \
-    }                                                                                          \
-  }                                                                                            \
+#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
 
 /*
@@ -192,6 +212,7 @@
   }
 
   list_init(&logger_list->logger);
+  list_init(&logger_list->transport);
   logger_list->mode = mode;
   logger_list->tail = tail;
   logger_list->pid = pid;
@@ -208,6 +229,7 @@
   }
 
   list_init(&logger_list->logger);
+  list_init(&logger_list->transport);
   logger_list->mode = mode;
   logger_list->start = start;
   logger_list->pid = pid;
@@ -225,27 +247,38 @@
   struct android_log_logger* logger;
 
   if (!logger_list_internal || (logId >= LOG_ID_MAX)) {
-    return nullptr;
+    goto err;
   }
 
   logger_for_each(logger, logger_list_internal) {
     if (logger->logId == logId) {
-      return reinterpret_cast<struct logger*>(logger);
+      goto ok;
     }
   }
 
   logger = static_cast<android_log_logger*>(calloc(1, sizeof(*logger)));
   if (!logger) {
-    return nullptr;
+    goto err;
   }
 
   logger->logId = logId;
   list_add_tail(&logger_list_internal->logger, &logger->node);
   logger->parent = logger_list_internal;
 
-  // Reset known transport to re-evaluate, since we added a new logger.
-  logger_list_internal->transport_initialized = false;
+  /* 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;
 }
 
@@ -272,24 +305,34 @@
                                   struct log_msg* log_msg) {
   int ret = (*transp->transport->read)(logger_list, transp, log_msg);
 
-  if (ret < 0) {
-    return ret;
-  }
-
   if (ret > (int)sizeof(*log_msg)) {
     ret = sizeof(*log_msg);
   }
 
-  if (ret < static_cast<int>(sizeof(log_msg->entry))) {
+  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;
   }
 
-  if (log_msg->entry.hdr_size != sizeof(log_msg->entry)) {
-    return -EINVAL;
-  }
-
-  if (log_msg->entry.len > ret - log_msg->entry.hdr_size) {
-    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;
@@ -297,6 +340,7 @@
 
 /* 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;
 
@@ -305,8 +349,84 @@
     return ret;
   }
 
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context;
-  return android_transport_read(logger_list_internal, transport_context, log_msg);
+  /* 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 */
@@ -318,10 +438,16 @@
     return;
   }
 
-  android_log_transport_context* transport_context = &logger_list_internal->transport_context;
+  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 (transport_context->transport && transport_context->transport->close) {
-    (*transport_context->transport->close)(logger_list_internal, transport_context);
+    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)) {
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index e1772f1..7fa3f43 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -25,31 +25,28 @@
 #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
 
-#if (FAKE_LOG_DEVICE == 0)
-extern struct android_log_transport_write logdLoggerWrite;
-extern struct android_log_transport_write pmsgLoggerWrite;
-
-android_log_transport_write* android_log_write = &logdLoggerWrite;
-android_log_transport_write* android_log_persist_write = &pmsgLoggerWrite;
-#else
-extern android_log_transport_write fakeLoggerWrite;
-
-android_log_transport_write* android_log_write = &fakeLoggerWrite;
-android_log_transport_write* android_log_persist_write = nullptr;
-#endif
-
 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();
@@ -100,13 +97,30 @@
   }
 
   for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-    if (i != LOG_ID_KERNEL && (i != LOG_ID_SECURITY || check_log_uid_permissions() == 0) &&
-        (*node->available)(static_cast<log_id_t>(i)) >= 0) {
+    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
@@ -115,6 +129,7 @@
  * 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
@@ -133,14 +148,20 @@
    * disengenuous use of this function.
    */
 
-  if (android_log_write != nullptr) {
-    android_log_write->close();
+  write_transport_for_each(transport, &__android_log_persist_write) {
+    if (transport->close) {
+      (*transport->close)();
+    }
   }
 
-  if (android_log_persist_write != nullptr) {
-    android_log_persist_write->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
@@ -164,39 +185,69 @@
 #endif
 }
 
-static bool transport_initialize(android_log_transport_write* transport) {
-  if (transport == nullptr) {
-    return false;
-  }
-
-  __android_log_cache_available(transport);
-  if (!transport->logMask) {
-    return false;
-  }
-
-  // TODO: Do we actually need to call close() if open() fails?
-  if (transport->open() < 0) {
-    transport->close();
-    return false;
-  }
-
-  return true;
-}
-
 /* log_init_lock assumed */
 static int __write_to_log_initialize() {
-  if (!transport_initialize(android_log_write)) {
+  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;
   }
 
-  transport_initialize(android_log_persist_write);
+  return ret;
+}
 
-  return 1;
+/*
+ * 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__)
@@ -252,7 +303,7 @@
       }
     }
     if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
-      tag = android_lookupEventTag_len(m, &len, *static_cast<uint32_t*>(vec[0].iov_base));
+      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 */
@@ -263,9 +314,30 @@
       return -EPERM;
     }
   } else {
-    int prio = *static_cast<int*>(vec[0].iov_base);
-    const char* tag = static_cast<const char*>(vec[1].iov_base);
-    size_t len = vec[1].iov_len;
+    /* 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;
@@ -283,18 +355,21 @@
 #endif
 
   ret = 0;
-  size_t i = 1 << log_id;
-
-  if (android_log_write != nullptr && (android_log_write->logMask & i)) {
-    ssize_t retval;
-    retval = android_log_write->write(log_id, &ts, vec, nr);
-    if (ret >= 0) {
-      ret = retval;
+  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;
+      }
     }
   }
 
-  if (android_log_persist_write != nullptr && (android_log_persist_write->logMask & i)) {
-    android_log_persist_write->write(log_id, &ts, vec, nr);
+  write_transport_for_each(node, &__android_log_persist_write) {
+    if (node->logMask & i) {
+      (void)(*node->write)(log_id, &ts, vec, nr);
+    }
   }
 
   errno = save_errno;
@@ -310,6 +385,9 @@
     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;
     }
@@ -329,15 +407,63 @@
 }
 
 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
 
-  struct iovec vec[3];
   vec[0].iov_base = (unsigned char*)&prio;
   vec[0].iov_len = 1;
   vec[1].iov_base = (void*)tag;
@@ -499,3 +625,82 @@
 
   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.cpp b/liblog/logprint.cpp
index 4b61828..bc056cb 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -26,19 +26,17 @@
 #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 <private/android_logger.h>
 
 #include "log_portability.h"
 
@@ -293,10 +291,8 @@
   return 1;
 }
 
-#ifndef __MINGW32__
 static const char tz[] = "TZ";
 static const char utc[] = "UTC";
-#endif
 
 /**
  * Returns FORMAT_OFF on invalid string
@@ -532,12 +528,18 @@
 
   int i;
   char* msg = buf->msg;
-  if (buf->hdr_size != sizeof(struct logger_entry)) {
-    fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
-    return -1;
+  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;
+    }
   }
-  entry->uid = buf->uid;
-
   for (i = 1; i < buf->len; i++) {
     if (msg[i] == '\0') {
       if (msgStart == -1) {
@@ -578,6 +580,24 @@
   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);
@@ -631,7 +651,8 @@
 
   if (eventDataLen < 1) return -1;
 
-  type = *eventData;
+  type = *eventData++;
+  eventDataLen--;
 
   cp = NULL;
   len = 0;
@@ -720,24 +741,22 @@
     case EVENT_TYPE_INT:
       /* 32-bit signed int */
       {
-        if (eventDataLen < sizeof(android_event_int_t)) return -1;
-        auto* event_int = reinterpret_cast<const android_event_int_t*>(eventData);
-        lval = event_int->data;
-        eventData += sizeof(android_event_int_t);
-        eventDataLen -= sizeof(android_event_int_t);
+        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 < sizeof(android_event_long_t)) {
-        return -1;
-      }
-      {
-        auto* event_long = reinterpret_cast<const android_event_long_t*>(eventData);
-        lval = event_long->data;
-      }
-      eventData += sizeof(android_event_long_t);
-      eventDataLen -= sizeof(android_event_long_t);
+      if (eventDataLen < 8) return -1;
+      lval = get8LE(eventData);
+      eventData += 8;
+      eventDataLen -= 8;
     pr_lval:
       outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
       if (outCount < outBufLen) {
@@ -751,11 +770,14 @@
     case EVENT_TYPE_FLOAT:
       /* float */
       {
-        if (eventDataLen < sizeof(android_event_float_t)) return -1;
-        auto* event_float = reinterpret_cast<const android_event_float_t*>(eventData);
-        float fval = event_float->data;
-        eventData += sizeof(android_event_int_t);
-        eventDataLen -= sizeof(android_event_int_t);
+        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) {
@@ -770,11 +792,12 @@
     case EVENT_TYPE_STRING:
       /* UTF-8 chars, not NULL-terminated */
       {
-        if (eventDataLen < sizeof(android_event_string_t)) return -1;
-        auto* event_string = reinterpret_cast<const android_event_string_t*>(eventData);
-        unsigned int strLen = event_string->length;
-        eventData += sizeof(android_event_string_t);
-        eventDataLen -= sizeof(android_event_string_t);
+        unsigned int strLen;
+
+        if (eventDataLen < 4) return -1;
+        strLen = get4LE(eventData);
+        eventData += 4;
+        eventDataLen -= 4;
 
         if (eventDataLen < strLen) {
           result = -1; /* mark truncated */
@@ -807,19 +830,20 @@
     case EVENT_TYPE_LIST:
       /* N items, all different types */
       {
-        if (eventDataLen < sizeof(android_event_list_t)) return -1;
-        auto* event_list = reinterpret_cast<const android_event_list_t*>(eventData);
+        unsigned char count;
+        int i;
 
-        int8_t count = event_list->element_count;
-        eventData += sizeof(android_event_list_t);
-        eventDataLen -= sizeof(android_event_list_t);
+        if (eventDataLen < 1) return -1;
+
+        count = *eventData++;
+        eventDataLen--;
 
         if (outBufLen <= 0) goto no_room;
 
         *outBuf++ = '[';
         outBufLen--;
 
-        for (int i = 0; i < count; i++) {
+        for (i = 0; i < count; i++) {
           result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
                                                 fmtStr, fmtLen);
           if (result != 0) goto bail;
@@ -987,21 +1011,32 @@
   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;
-  if (buf->hdr_size != sizeof(struct logger_entry)) {
-    fprintf(stderr, "+++ LOG: entry illegal hdr_size\n");
-    return -1;
+  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;
+    }
   }
-  if (buf->lid == LOG_ID_SECURITY) {
-    entry->priority = ANDROID_LOG_WARN;
-  }
-  entry->uid = buf->uid;
   inCount = buf->len;
-  if (inCount < sizeof(android_event_header_t)) return -1;
-  auto* event_header = reinterpret_cast<const android_event_header_t*>(eventData);
-  tagIndex = event_header->tag;
-  eventData += sizeof(android_event_header_t);
-  inCount -= sizeof(android_event_header_t);
+  if (inCount < 4) return -1;
+  tagIndex = get4LE(eventData);
+  eventData += 4;
+  inCount -= 4;
 
   entry->tagLen = 0;
   entry->tag = NULL;
@@ -1051,6 +1086,9 @@
   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;
@@ -1096,13 +1134,66 @@
 }
 
 /*
+ * 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.
  */
-size_t convertPrintable(char* p, const char* message, size_t messageLen) {
+static 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];
@@ -1110,10 +1201,11 @@
     if ((size_t)len > messageLen) {
       len = messageLen;
     }
-    len = mbrtowc(nullptr, message, len, &mb_state);
+    len = utf8_character_length(message, len);
 
     if (len < 0) {
-      snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
+      snprintf(buf, sizeof(buf), ((messageLen > 1) && isdigit(message[1])) ? "\\%03o" : "\\%o",
+               *message & 0377);
       len = 1;
     } else {
       buf[0] = '\0';
@@ -1133,7 +1225,7 @@
         } else if (*message == '\\') {
           strcpy(buf, "\\\\");
         } else if ((*message < ' ') || (*message & 0x80)) {
-          snprintf(buf, sizeof(buf), "\\x%02X", static_cast<unsigned char>(*message));
+          snprintf(buf, sizeof(buf), "\\%o", *message & 0377);
         }
       }
       if (!buf[0]) {
@@ -1151,7 +1243,6 @@
   return p - begin;
 }
 
-#ifdef __ANDROID__
 static char* readSeconds(char* e, struct timespec* t) {
   unsigned long multiplier;
   char* p;
@@ -1192,6 +1283,7 @@
   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 {
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index f43ce3a..ba27fd7 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -24,6 +25,7 @@
 #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);
@@ -37,12 +39,13 @@
                      struct android_log_transport_context* transp);
 
 struct android_log_transport_read pmsgLoggerRead = {
+    .node = {&pmsgLoggerRead.node, &pmsgLoggerRead.node},
     .name = "pmsg",
     .available = pmsgAvailable,
     .version = pmsgVersion,
-    .close = pmsgClose,
     .read = pmsgRead,
     .poll = NULL,
+    .close = pmsgClose,
     .clear = pmsgClear,
     .setSize = NULL,
     .getSize = NULL,
@@ -62,9 +65,58 @@
   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) {
-  return unlink("/sys/fs/pstore/pmsg-ramoops-0");
+  if (uid_has_log_permission(get_best_effective_uid())) {
+    return unlink("/sys/fs/pstore/pmsg-ramoops-0");
+  }
+  errno = EPERM;
+  return -1;
 }
 
 /*
@@ -79,12 +131,15 @@
                     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));
 
@@ -144,30 +199,37 @@
           ((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))) {
-      char* msg = log_msg->entry.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;
-      }
+      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.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
-      log_msg->entry.hdr_size = sizeof(log_msg->entry);
-      log_msg->entry.pid = buf.p.pid;
-      log_msg->entry.tid = buf.l.tid;
-      log_msg->entry.sec = buf.l.realtime.tv_sec;
-      log_msg->entry.nsec = buf.l.realtime.tv_nsec;
-      log_msg->entry.lid = buf.l.id;
-      log_msg->entry.uid = buf.p.uid;
+        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.hdr_size;
+        return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
+      }
     }
 
     fd = atomic_load(&transp->context.fd);
@@ -215,7 +277,13 @@
   struct android_log_transport_context transp;
   struct content {
     struct listnode node;
-    struct logger_entry entry;
+    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;
@@ -267,26 +335,25 @@
   }
 
   /* Read the file content */
-  log_msg log_msg;
-  while (pmsgRead(&logger_list, &transp, &log_msg) > 0) {
+  while (pmsgRead(&logger_list, &transp, &transp.logMsg) > 0) {
     const char* cp;
-    size_t hdr_size = log_msg.entry.hdr_size;
-
-    char* msg = (char*)&log_msg + hdr_size;
+    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(log_msg.entry)) {
+    if ((hdr_size < sizeof(transp.logMsg.entry_v1)) || (hdr_size > sizeof(transp.logMsg.entry))) {
       continue;
     }
     /* Check for invalid sequence number */
-    if (log_msg.entry.nsec % ANDROID_LOG_PMSG_FILE_SEQUENCE ||
-        (log_msg.entry.nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >=
-            ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
+    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 = log_msg.entry.len - sizeof(prio);
+    len = transp.logMsg.entry.len - sizeof(prio);
     for (cp = msg + sizeof(prio); *cp && isprint(*cp) && !isspace(*cp) && --len; ++cp) {
       if (*cp == ':') {
         if (split) {
@@ -332,8 +399,8 @@
     /* 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 == log_msg.entry.lid &&
-          names->prio == *msg) {
+      if (!strcmp(names->name, msg + sizeof(prio)) && (names->id == transp.logMsg.entry.lid) &&
+          (names->prio == *msg)) {
         break;
       }
     }
@@ -350,7 +417,7 @@
         break;
       }
       strcpy(names->name, msg + sizeof(prio));
-      names->id = static_cast<log_id_t>(log_msg.entry.lid);
+      names->id = static_cast<log_id_t>(transp.logMsg.entry.lid);
       names->prio = *msg;
       list_init(&names->content);
       /*
@@ -403,7 +470,7 @@
     /* 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 (log_msg.entry.nsec == content->entry.nsec) {
+      if (transp.logMsg.entry.nsec == content->entry.nsec) {
         list_remove(&content->node);
         free(content);
       }
@@ -411,16 +478,16 @@
 
     /* Add content */
     content = static_cast<struct content*>(
-        calloc(1, sizeof(content->node) + hdr_size + log_msg.entry.len));
+        calloc(1, sizeof(content->node) + hdr_size + transp.logMsg.entry.len));
     if (!content) {
       ret = -ENOMEM;
       break;
     }
-    memcpy(&content->entry, &log_msg.entry, hdr_size + log_msg.entry.len);
+    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 < log_msg.entry.nsec) {
+      if ((node_to_item(node, struct content, node))->entry.nsec < transp.logMsg.entry.nsec) {
         break;
       }
     }
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 54980d9..e851100 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -20,6 +20,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
@@ -29,6 +30,7 @@
 #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"
@@ -39,9 +41,9 @@
 static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
 
 struct android_log_transport_write pmsgLoggerWrite = {
-    .name = "pmsg",
-    .logMask = 0,
+    .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
     .context.fd = -1,
+    .name = "pmsg",
     .available = pmsgAvailable,
     .open = pmsgOpen,
     .close = pmsgClose,
@@ -86,6 +88,13 @@
   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];
@@ -99,7 +108,7 @@
       return -EINVAL;
     }
 
-    if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
+    if (SNET_EVENT_LOG_TAG != get4LE(static_cast<uint8_t*>(vec[0].iov_base))) {
       return -EPERM;
     }
   }
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
index 394fa93..50755ce 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -54,21 +54,20 @@
     ],
     srcs: [
         "libc_test.cpp",
-        "liblog_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"],
-    isolated: true,
 }
 
 // Build tests for the device (with .so). Run with:
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 56892a2..7d11ccf 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -17,7 +17,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <poll.h>
-#include <sched.h>
+#include <sys/endian.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -30,6 +30,7 @@
 #include <benchmark/benchmark.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
+#include <log/log_transport.h>
 #include <private/android_logger.h>
 
 BENCHMARK_MAIN();
@@ -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", "%" PRIu64,
-                                          state.iterations()));
+    LOG_FAILURE_RETRY(__android_log_print(
+        ANDROID_LOG_INFO, "BM_log_maximum_retry", "%zu", state.iterations()));
   }
 }
 BENCHMARK(BM_log_maximum_retry);
@@ -68,11 +69,27 @@
  */
 static void BM_log_maximum(benchmark::State& state) {
   while (state.KeepRunning()) {
-    __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%" PRIu64, state.iterations());
+    __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%zu",
+                        state.iterations());
   }
 }
 BENCHMARK(BM_log_maximum);
 
+static void set_log_null() {
+  android_set_log_transport(LOGGER_NULL);
+}
+
+static void set_log_default() {
+  android_set_log_transport(LOGGER_DEFAULT);
+}
+
+static void BM_log_maximum_null(benchmark::State& state) {
+  set_log_null();
+  BM_log_maximum(state);
+  set_log_default();
+}
+BENCHMARK(BM_log_maximum_null);
+
 /*
  *	Measure the time it takes to collect the time using
  * discrete acquisition (state.PauseTiming() to state.ResumeTiming())
@@ -211,14 +228,14 @@
   buffer.header.tag = 0;
   buffer.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer.payload.data = snapshot;
+  buffer.payload.data = htole32(snapshot);
 
   newVec[2].iov_base = &buffer;
   newVec[2].iov_len = sizeof(buffer);
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer.payload.data = snapshot;
+    buffer.payload.data = htole32(snapshot);
     writev(pstore_fd, newVec, nr);
   }
   state.PauseTiming();
@@ -269,7 +286,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=%" PRIu64 "\n", &buffer->pmsg_header,
+    fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
             state.iterations());
   }
 
@@ -287,11 +304,11 @@
   buffer->payload.header.tag = 0;
   buffer->payload.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer->payload.payload.data = snapshot;
+  buffer->payload.payload.data = htole32(snapshot);
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer->payload.payload.data = snapshot;
+    buffer->payload.payload.data = htole32(snapshot);
     write(pstore_fd, &buffer->pmsg_header,
           sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
               sizeof(android_log_event_int_t));
@@ -344,7 +361,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=%" PRIu64 "\n", &buffer->pmsg_header,
+    fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
             state.iterations());
   }
 
@@ -362,11 +379,11 @@
   buffer->payload.header.tag = 0;
   buffer->payload.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer->payload.payload.data = snapshot;
+  buffer->payload.payload.data = htole32(snapshot);
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer->payload.payload.data = snapshot;
+    buffer->payload.payload.data = htole32(snapshot);
     write(pstore_fd, &buffer->pmsg_header,
           sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
               sizeof(android_log_event_int_t));
@@ -419,7 +436,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=%" PRIu64 "\n", &buffer->pmsg_header,
+    fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
             state.iterations());
   }
 
@@ -437,11 +454,11 @@
   buffer->payload.header.tag = 0;
   buffer->payload.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer->payload.payload.data = snapshot;
+  buffer->payload.payload.data = htole32(snapshot);
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer->payload.payload.data = snapshot;
+    buffer->payload.payload.data = htole32(snapshot);
     write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
   }
   state.PauseTiming();
@@ -492,7 +509,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=%" PRIu64 "\n", &buffer->pmsg_header,
+    fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
             state.iterations());
   }
 
@@ -510,11 +527,11 @@
   buffer->payload.header.tag = 0;
   buffer->payload.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer->payload.payload.data = snapshot;
+  buffer->payload.payload.data = htole32(snapshot);
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer->payload.payload.data = snapshot;
+    buffer->payload.payload.data = htole32(snapshot);
     write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
   }
   state.PauseTiming();
@@ -543,7 +560,7 @@
 /* performance test */
 static void BM_sprintf_overhead(benchmark::State& state) {
   while (state.KeepRunning()) {
-    test_print("BM_sprintf_overhead:%" PRIu64, state.iterations());
+    test_print("BM_sprintf_overhead:%zu", state.iterations());
     state.PauseTiming();
     logd_yield();
     state.ResumeTiming();
@@ -558,7 +575,8 @@
  */
 static void BM_log_print_overhead(benchmark::State& state) {
   while (state.KeepRunning()) {
-    __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%" PRIu64, state.iterations());
+    __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%zu",
+                        state.iterations());
     state.PauseTiming();
     logd_yield();
     state.ResumeTiming();
@@ -603,6 +621,13 @@
 }
 BENCHMARK(BM_log_event_overhead_42);
 
+static void BM_log_event_overhead_null(benchmark::State& state) {
+  set_log_null();
+  BM_log_event_overhead(state);
+  set_log_default();
+}
+BENCHMARK(BM_log_event_overhead_null);
+
 /*
  *	Measure the time it takes to submit the android event logging call
  * using discrete acquisition under very-light load (<1% CPU utilization).
@@ -617,6 +642,15 @@
 }
 BENCHMARK(BM_log_light_overhead);
 
+static void BM_log_light_overhead_null(benchmark::State& state) {
+  set_log_null();
+  BM_log_light_overhead(state);
+  set_log_default();
+}
+// Default gets out of hand for this test, so we set a reasonable number of
+// iterations for a timely result.
+BENCHMARK(BM_log_light_overhead_null)->Iterations(500);
+
 static void caught_latency(int /*signum*/) {
   unsigned long long v = 0xDEADBEEFA55A5AA5ULL;
 
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index c402e20..1f87b3e 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -27,12 +27,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <memory>
 #include <string>
 
 #include <android-base/file.h>
 #include <android-base/macros.h>
-#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #ifdef __ANDROID__  // includes sys/properties.h which does not exist outside
 #include <cutils/properties.h>
@@ -40,13 +38,25 @@
 #include <gtest/gtest.h>
 #include <log/log_event_list.h>
 #include <log/log_properties.h>
+#include <log/log_transport.h>
 #include <log/logprint.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-using android::base::make_scope_guard;
+#ifndef TEST_PREFIX
+#ifdef TEST_LOGGER
+#define TEST_PREFIX android_set_log_transport(TEST_LOGGER);
+// make sure we always run code despite overrides if compiled for android
+#elif defined(__ANDROID__)
+#define TEST_PREFIX
+#endif
+#endif
 
-// #define ENABLE_FLAKY_TESTS
+#ifdef USING_LOGGER_STDERR
+#define SUPPORTS_END_TO_END 0
+#else
+#define SUPPORTS_END_TO_END 1
+#endif
 
 // enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
 // non-syscall libs. Since we are only using this in the emergency of
@@ -62,93 +72,24 @@
     _rc;                                                                 \
   })
 
-// std::unique_ptr doesn't let you provide a pointer to a deleter (android_logger_list_close()) if
-// the type (struct logger_list) is an incomplete type, so we create ListCloser instead.
-struct ListCloser {
-  void operator()(struct logger_list* list) { android_logger_list_close(list); }
-};
-
-// This function is meant to be used for most log tests, it does the following:
-// 1) Open the log_buffer with a blocking reader
-// 2) Write the messages via write_messages
-// 3) Set an alarm for 2 seconds as a timeout
-// 4) Read until check_message returns true, which should be used to indicate the target message
-//    is found
-// 5) Open log_buffer with a non_blocking reader and dump all messages
-// 6) Count the number of times check_messages returns true for these messages and assert it's
-//    only 1.
-template <typename FWrite, typename FCheck>
-static void RunLogTests(log_id_t log_buffer, FWrite write_messages, FCheck check_message) {
-  pid_t pid = getpid();
-
-  auto logger_list = std::unique_ptr<struct logger_list, ListCloser>{
-      android_logger_list_open(log_buffer, ANDROID_LOG_RDONLY, 1000, pid)};
-  ASSERT_TRUE(logger_list);
-
-  write_messages();
-
-  alarm(2);
-  auto alarm_guard = android::base::make_scope_guard([] { alarm(0); });
-  bool found = false;
-  while (!found) {
-    log_msg log_msg;
-    ASSERT_GT(android_logger_list_read(logger_list.get(), &log_msg), 0);
-
-    ASSERT_EQ(log_buffer, log_msg.id());
-    ASSERT_EQ(pid, log_msg.entry.pid);
-
-    // TODO: Should this be an assert?
-    if (log_msg.msg() == nullptr) {
-      continue;
-    }
-
-    check_message(log_msg, &found);
-  }
-
-  auto logger_list_non_block = std::unique_ptr<struct logger_list, ListCloser>{
-      android_logger_list_open(log_buffer, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)};
-  ASSERT_TRUE(logger_list_non_block);
-
-  size_t count = 0;
-  while (true) {
-    log_msg log_msg;
-    auto ret = android_logger_list_read(logger_list_non_block.get(), &log_msg);
-    if (ret == -EAGAIN) {
-      break;
-    }
-    ASSERT_GT(ret, 0);
-
-    ASSERT_EQ(log_buffer, log_msg.id());
-    ASSERT_EQ(pid, log_msg.entry.pid);
-
-    // TODO: Should this be an assert?
-    if (log_msg.msg() == nullptr) {
-      continue;
-    }
-
-    found = false;
-    check_message(log_msg, &found);
-    if (found) {
-      ++count;
-    }
-  }
-
-  EXPECT_EQ(1U, count);
-}
-
 TEST(liblog, __android_log_btwrite) {
+#ifdef TEST_PREFIX
+  TEST_PREFIX
+#endif
   int intBuf = 0xDEADBEEF;
   EXPECT_LT(0,
             __android_log_btwrite(0, EVENT_TYPE_INT, &intBuf, sizeof(intBuf)));
   long long longBuf = 0xDEADBEEFA55A5AA5;
   EXPECT_LT(
       0, __android_log_btwrite(0, EVENT_TYPE_LONG, &longBuf, sizeof(longBuf)));
+  usleep(1000);
   char Buf[] = "\20\0\0\0DeAdBeEfA55a5aA5";
   EXPECT_LT(0,
             __android_log_btwrite(0, EVENT_TYPE_STRING, Buf, sizeof(Buf) - 1));
+  usleep(1000);
 }
 
-#if defined(__ANDROID__)
+#if (defined(__ANDROID__) && defined(USING_LOGGER_DEFAULT))
 static std::string popenToString(const std::string& command) {
   std::string ret;
 
@@ -219,60 +160,80 @@
 
 TEST(liblog, __android_log_btwrite__android_logger_list_read) {
 #ifdef __ANDROID__
+#ifdef TEST_PREFIX
+  TEST_PREFIX
+#endif
+  struct logger_list* logger_list;
+
+  pid_t pid = getpid();
+
+  ASSERT_TRUE(NULL !=
+              (logger_list = android_logger_list_open(
+                   LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                   1000, pid)));
+
   log_time ts(CLOCK_MONOTONIC);
-  log_time ts1(ts);
+  EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+#ifdef USING_LOGGER_DEFAULT
+  // Check that we can close and reopen the logger
+  bool logdwActiveAfter__android_log_btwrite;
+  if (getuid() == AID_ROOT) {
+    tested__android_log_close = true;
+#ifndef NO_PSTORE
+    bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+    EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+#endif /* NO_PSTORE */
+    logdwActiveAfter__android_log_btwrite = isLogdwActive();
+    EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+  } else if (!tested__android_log_close) {
+    fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+  }
+  __android_log_close();
+  if (getuid() == AID_ROOT) {
+#ifndef NO_PSTORE
+    bool pmsgActiveAfter__android_log_close = isPmsgActive();
+    EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+#endif /* NO_PSTORE */
+    bool logdwActiveAfter__android_log_close = isLogdwActive();
+    EXPECT_FALSE(logdwActiveAfter__android_log_close);
+  }
+#endif
 
-  auto write_function = [&] {
-    EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
-    // Check that we can close and reopen the logger
-    bool logdwActiveAfter__android_log_btwrite;
-    if (getuid() == AID_ROOT) {
-      tested__android_log_close = true;
+  log_time ts1(CLOCK_MONOTONIC);
+  EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+#ifdef USING_LOGGER_DEFAULT
+  if (getuid() == AID_ROOT) {
 #ifndef NO_PSTORE
-      bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-      EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+    bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+    EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
 #endif /* NO_PSTORE */
-      logdwActiveAfter__android_log_btwrite = isLogdwActive();
-      EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
-    } else if (!tested__android_log_close) {
-      fprintf(stderr, "WARNING: can not test __android_log_close()\n");
-    }
-    __android_log_close();
-    if (getuid() == AID_ROOT) {
-#ifndef NO_PSTORE
-      bool pmsgActiveAfter__android_log_close = isPmsgActive();
-      EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-#endif /* NO_PSTORE */
-      bool logdwActiveAfter__android_log_close = isLogdwActive();
-      EXPECT_FALSE(logdwActiveAfter__android_log_close);
-    }
-
-    ts1 = log_time(CLOCK_MONOTONIC);
-    EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
-    if (getuid() == AID_ROOT) {
-#ifndef NO_PSTORE
-      bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-      EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
-#endif /* NO_PSTORE */
-      logdwActiveAfter__android_log_btwrite = isLogdwActive();
-      EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
-    }
-  };
+    logdwActiveAfter__android_log_btwrite = isLogdwActive();
+    EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+  }
+#endif
+  usleep(1000000);
 
   int count = 0;
   int second_count = 0;
 
-  auto check_function = [&](log_msg log_msg, bool* found) {
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
+    }
+
+    EXPECT_EQ(log_msg.entry.pid, pid);
+
     if ((log_msg.entry.len != sizeof(android_log_event_long_t)) ||
         (log_msg.id() != LOG_ID_EVENTS)) {
-      return;
+      continue;
     }
 
     android_log_event_long_t* eventData;
     eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
     if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
-      return;
+      continue;
     }
 
     log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
@@ -281,61 +242,171 @@
     } else if (ts1 == tx) {
       ++second_count;
     }
+  }
 
-    if (count == 1 && second_count == 1) {
-      count = 0;
-      second_count = 0;
-      *found = true;
-    }
-  };
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
+  EXPECT_EQ(SUPPORTS_END_TO_END, second_count);
 
-  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
-
+  android_logger_list_close(logger_list);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
-TEST(liblog, __android_log_write__android_logger_list_read) {
 #ifdef __ANDROID__
+static void print_transport(const char* prefix, int logger) {
+  static const char orstr[] = " | ";
+
+  if (!prefix) {
+    prefix = "";
+  }
+  if (logger < 0) {
+    fprintf(stderr, "%s%s\n", prefix, strerror(-logger));
+    return;
+  }
+
+  if (logger == LOGGER_DEFAULT) {
+    fprintf(stderr, "%sLOGGER_DEFAULT", prefix);
+    prefix = orstr;
+  }
+  if (logger & LOGGER_LOGD) {
+    fprintf(stderr, "%sLOGGER_LOGD", prefix);
+    prefix = orstr;
+  }
+  if (logger & LOGGER_KERNEL) {
+    fprintf(stderr, "%sLOGGER_KERNEL", prefix);
+    prefix = orstr;
+  }
+  if (logger & LOGGER_NULL) {
+    fprintf(stderr, "%sLOGGER_NULL", prefix);
+    prefix = orstr;
+  }
+  if (logger & LOGGER_STDERR) {
+    fprintf(stderr, "%sLOGGER_STDERR", prefix);
+    prefix = orstr;
+  }
+  logger &= ~(LOGGER_LOGD | LOGGER_KERNEL | LOGGER_NULL | LOGGER_STDERR);
+  if (logger) {
+    fprintf(stderr, "%s0x%x", prefix, logger);
+    prefix = orstr;
+  }
+  if (prefix == orstr) {
+    fprintf(stderr, "\n");
+  }
+}
+#endif
+
+// This test makes little sense standalone, and requires the tests ahead
+// 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) {
+#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+  TEST_PREFIX
+#endif
+
+  int logger = android_get_log_transport();
+  print_transport("android_get_log_transport = ", logger);
+  EXPECT_NE(LOGGER_NULL, logger);
+
+  int ret;
+  EXPECT_EQ(LOGGER_NULL, ret = android_set_log_transport(LOGGER_NULL));
+  print_transport("android_set_log_transport = ", ret);
+  EXPECT_EQ(LOGGER_NULL, ret = android_get_log_transport());
+  print_transport("android_get_log_transport = ", ret);
+
   pid_t pid = getpid();
 
-  struct timespec ts;
-  clock_gettime(CLOCK_MONOTONIC, &ts);
-  std::string buf = android::base::StringPrintf("pid=%u ts=%ld.%09ld", pid, ts.tv_sec, ts.tv_nsec);
-  static const char tag[] = "liblog.__android_log_write__android_logger_list_read";
-  static const char prio = ANDROID_LOG_DEBUG;
+  struct logger_list* logger_list;
+  ASSERT_TRUE(NULL !=
+              (logger_list = android_logger_list_open(
+                   LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                   1000, pid)));
 
-  std::string expected_message =
-      std::string(&prio, sizeof(prio)) + tag + std::string("", 1) + buf + std::string("", 1);
+  log_time ts(CLOCK_MONOTONIC);
+  EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
 
-  auto write_function = [&] { ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str())); };
+  usleep(1000000);
 
-  auto check_function = [&](log_msg log_msg, bool* found) {
-    if (log_msg.entry.len != expected_message.length()) {
-      return;
+  int count = 0;
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
     }
 
-    if (expected_message != std::string(log_msg.msg(), log_msg.entry.len)) {
-      return;
+    EXPECT_EQ(log_msg.entry.pid, pid);
+
+    if ((log_msg.entry.len != sizeof(android_log_event_long_t)) ||
+        (log_msg.id() != LOG_ID_EVENTS)) {
+      continue;
     }
 
-    *found = true;
-  };
+    android_log_event_long_t* eventData;
+    eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-  RunLogTests(LOG_ID_MAIN, write_function, check_function);
+    if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+      continue;
+    }
 
+    log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+    if (ts == tx) {
+      ++count;
+    }
+  }
+
+  android_logger_list_close(logger_list);
+
+  EXPECT_EQ(logger, ret = android_set_log_transport(logger));
+  print_transport("android_set_log_transport = ", ret);
+  EXPECT_EQ(logger, ret = android_get_log_transport());
+  print_transport("android_get_log_transport = ", ret);
+
+  // False negative if liblog.__android_log_btwrite__android_logger_list_read
+  // fails above, so we will likely succeed. But we will have so many
+  // failures elsewhere that it is probably not worthwhile for us to
+  // highlight yet another disappointment.
+  //
+  // We also expect failures in the following tests if the set does not
+  // react in an appropriate manner internally, yet passes, so we depend
+  // on this test being in the middle of a series of tests performed in
+  // the same process.
+  EXPECT_EQ(0, count);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
+#ifdef TEST_PREFIX
+static inline uint32_t get4LE(const uint8_t* src) {
+  return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+static inline uint32_t get4LE(const char* src) {
+  return get4LE(reinterpret_cast<const uint8_t*>(src));
+}
+#endif
+
 static void bswrite_test(const char* message) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+  TEST_PREFIX
+  struct logger_list* logger_list;
+
   pid_t pid = getpid();
 
-  log_time ts(android_log_clockid());
+  ASSERT_TRUE(NULL !=
+              (logger_list = android_logger_list_open(
+                   LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                   1000, pid)));
 
+#ifdef __ANDROID__
+  log_time ts(android_log_clockid());
+#else
+  log_time ts(CLOCK_REALTIME);
+#endif
+
+  EXPECT_LT(0, __android_log_bswrite(0, message));
   size_t num_lines = 1, size = 0, length = 0, total = 0;
   const char* cp = message;
   while (*cp) {
@@ -357,25 +428,36 @@
     ++cp;
     ++total;
   }
+  usleep(1000000);
 
-  auto write_function = [&] { EXPECT_LT(0, __android_log_bswrite(0, message)); };
+  int count = 0;
 
-  auto check_function = [&](log_msg log_msg, bool* found) {
-    if ((size_t)log_msg.entry.len != (sizeof(android_log_event_string_t) + length) ||
-        log_msg.id() != LOG_ID_EVENTS) {
-      return;
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
+    }
+
+    EXPECT_EQ(log_msg.entry.pid, pid);
+
+    if ((log_msg.entry.sec < (ts.tv_sec - 1)) ||
+        ((ts.tv_sec + 1) < log_msg.entry.sec) ||
+        ((size_t)log_msg.entry.len !=
+         (sizeof(android_log_event_string_t) + length)) ||
+        (log_msg.id() != LOG_ID_EVENTS)) {
+      continue;
     }
 
     android_log_event_string_t* eventData;
     eventData = reinterpret_cast<android_log_event_string_t*>(log_msg.msg());
 
     if (!eventData || (eventData->type != EVENT_TYPE_STRING)) {
-      return;
+      continue;
     }
 
-    size_t len = eventData->length;
+    size_t len = get4LE(reinterpret_cast<char*>(&eventData->length));
     if (len == total) {
-      *found = true;
+      ++count;
 
       AndroidLogFormat* logformat = android_log_format_new();
       EXPECT_TRUE(NULL != logformat);
@@ -385,7 +467,7 @@
         fprintf(stderr, "Expect \"Binary log entry conversion failed\"\n");
       }
       int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
-          &log_msg.entry, &entry, nullptr, msgBuf, sizeof(msgBuf));
+          &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
       EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
       if ((processBinaryLogBuffer == 0) || entry.message) {
         size_t line_overhead = 20;
@@ -402,10 +484,11 @@
       }
       android_log_format_free(logformat);
     }
-  };
+  }
 
-  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
 
+  android_logger_list_close(logger_list);
 #else
   message = NULL;
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -433,15 +516,26 @@
 }
 
 static void buf_write_test(const char* message) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+  TEST_PREFIX
+  struct logger_list* logger_list;
+
   pid_t pid = getpid();
 
-  static const char tag[] = "TEST__android_log_buf_write";
-  log_time ts(android_log_clockid());
+  ASSERT_TRUE(
+      NULL !=
+      (logger_list = android_logger_list_open(
+           LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
-  auto write_function = [&] {
-    EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
-  };
+  static const char tag[] = "TEST__android_log_buf_write";
+#ifdef __ANDROID__
+  log_time ts(android_log_clockid());
+#else
+  log_time ts(CLOCK_REALTIME);
+#endif
+
+  EXPECT_LT(
+      0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO, tag, message));
   size_t num_lines = 1, size = 0, length = 0;
   const char* cp = message;
   while (*cp) {
@@ -458,18 +552,32 @@
     }
     ++cp;
   }
+  usleep(1000000);
 
-  auto check_function = [&](log_msg log_msg, bool* found) {
-    if ((size_t)log_msg.entry.len != (sizeof(tag) + length + 2) || log_msg.id() != LOG_ID_MAIN) {
-      return;
+  int count = 0;
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
     }
 
-    *found = true;
+    ASSERT_EQ(log_msg.entry.pid, pid);
+
+    if ((log_msg.entry.sec < (ts.tv_sec - 1)) ||
+        ((ts.tv_sec + 1) < log_msg.entry.sec) ||
+        ((size_t)log_msg.entry.len != (sizeof(tag) + length + 2)) ||
+        (log_msg.id() != LOG_ID_MAIN)) {
+      continue;
+    }
+
+    ++count;
 
     AndroidLogFormat* logformat = android_log_format_new();
     EXPECT_TRUE(NULL != logformat);
     AndroidLogEntry entry;
-    int processLogBuffer = android_log_processLogBuffer(&log_msg.entry, &entry);
+    int processLogBuffer =
+        android_log_processLogBuffer(&log_msg.entry_v1, &entry);
     EXPECT_EQ(0, processLogBuffer);
     if (processLogBuffer == 0) {
       size_t line_overhead = 11;
@@ -480,10 +588,11 @@
                 android_log_printLogLine(logformat, fileno(stderr), &entry));
     }
     android_log_format_free(logformat);
-  };
+  }
 
-  RunLogTests(LOG_ID_MAIN, write_function, check_function);
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
 
+  android_logger_list_close(logger_list);
 #else
   message = NULL;
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -502,8 +611,8 @@
   buf_write_test("\n Hello World \n");
 }
 
-#ifdef ENABLE_FLAKY_TESTS
-#ifdef __ANDROID__
+#ifdef USING_LOGGER_DEFAULT  // requires blocking reader functionality
+#ifdef TEST_PREFIX
 static unsigned signaled;
 static log_time signal_time;
 
@@ -564,7 +673,8 @@
 #endif
 
 TEST(liblog, android_logger_list_read__cpu_signal) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+  TEST_PREFIX
   struct logger_list* logger_list;
   unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
@@ -656,7 +766,7 @@
 #endif
 }
 
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
 /*
  *  Strictly, we are not allowed to log messages in a signal context, the
  * correct way to handle this is to ensure the messages are constructed in
@@ -720,7 +830,8 @@
 #endif
 
 TEST(liblog, android_logger_list_read__cpu_thread) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+  TEST_PREFIX
   struct logger_list* logger_list;
   unsigned long long v = 0xDEADBEAFA55A0000ULL;
 
@@ -812,8 +923,13 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // ENABLE_FLAKY_TESTS
+#endif  // USING_LOGGER_DEFAULT
 
+#ifdef TEST_PREFIX
+static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
+#define SIZEOF_MAX_PAYLOAD_BUF \
+  (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(max_payload_tag) - 1)
+#endif
 static const char max_payload_buf[] =
     "LEONATO\n\
 I learn in this letter that Don Peter of Arragon\n\
@@ -947,26 +1063,39 @@
 takes his leave.";
 
 TEST(liblog, max_payload) {
-#ifdef __ANDROID__
-  static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
-#define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(max_payload_tag) - 1)
-
+#ifdef TEST_PREFIX
+  TEST_PREFIX
   pid_t pid = getpid();
   char tag[sizeof(max_payload_tag)];
   memcpy(tag, max_payload_tag, sizeof(tag));
   snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
 
-  auto write_function = [&] {
-    LOG_FAILURE_RETRY(
-        __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, max_payload_buf));
-  };
+  LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+                                            tag, max_payload_buf));
+  sleep(2);
 
+  struct logger_list* logger_list;
+
+  ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+                           LOG_ID_SYSTEM, ANDROID_LOG_RDONLY, 100, 0)));
+
+  bool matches = false;
   ssize_t max_len = 0;
-  auto check_function = [&](log_msg log_msg, bool* found) {
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
+    }
+
+    if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
+      continue;
+    }
+
     char* data = log_msg.msg();
 
     if (!data || strcmp(++data, tag)) {
-      return;
+      continue;
     }
 
     data += strlen(data) + 1;
@@ -983,36 +1112,71 @@
     }
 
     if (max_len > 512) {
-      *found = true;
+      matches = true;
+      break;
     }
-  };
+  }
 
-  RunLogTests(LOG_ID_SYSTEM, write_function, check_function);
+  android_logger_list_close(logger_list);
+
+#if SUPPORTS_END_TO_END
+  EXPECT_EQ(true, matches);
 
   EXPECT_LE(SIZEOF_MAX_PAYLOAD_BUF, static_cast<size_t>(max_len));
 #else
+  EXPECT_EQ(false, matches);
+#endif
+#else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 TEST(liblog, __android_log_buf_print__maxtag) {
-#ifdef __ANDROID__
-  auto write_function = [&] {
-    EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, max_payload_buf,
-                                         max_payload_buf));
-  };
+#ifdef TEST_PREFIX
+  TEST_PREFIX
+  struct logger_list* logger_list;
 
-  auto check_function = [&](log_msg log_msg, bool* found) {
-    if ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD) {
-      return;
+  pid_t pid = getpid();
+
+  ASSERT_TRUE(
+      NULL !=
+      (logger_list = android_logger_list_open(
+           LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+#ifdef __ANDROID__
+  log_time ts(android_log_clockid());
+#else
+  log_time ts(CLOCK_REALTIME);
+#endif
+
+  EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+                                       max_payload_buf, max_payload_buf));
+  usleep(1000000);
+
+  int count = 0;
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
     }
 
-    *found = true;
+    ASSERT_EQ(log_msg.entry.pid, pid);
+
+    if ((log_msg.entry.sec < (ts.tv_sec - 1)) ||
+        ((ts.tv_sec + 1) < log_msg.entry.sec) ||
+        ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD) ||
+        (log_msg.id() != LOG_ID_MAIN)) {
+      continue;
+    }
+
+    ++count;
 
     AndroidLogFormat* logformat = android_log_format_new();
     EXPECT_TRUE(NULL != logformat);
     AndroidLogEntry entry;
-    int processLogBuffer = android_log_processLogBuffer(&log_msg.entry, &entry);
+    int processLogBuffer =
+        android_log_processLogBuffer(&log_msg.entry_v1, &entry);
     EXPECT_EQ(0, processLogBuffer);
     if (processLogBuffer == 0) {
       fflush(stderr);
@@ -1024,20 +1188,19 @@
       EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD * 13 / 8, printLogLine);
     }
     android_log_format_free(logformat);
-  };
+  }
 
-  RunLogTests(LOG_ID_MAIN, write_function, check_function);
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
 
+  android_logger_list_close(logger_list);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
-// TODO: This test is tautological. android_logger_list_read() calls recv() with
-// LOGGER_ENTRY_MAX_PAYLOAD as its size argument, so it's not possible for this test to read a
-// payload larger than that size.
 TEST(liblog, too_big_payload) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+  TEST_PREFIX
   pid_t pid = getpid();
   static const char big_payload_tag[] = "TEST_big_payload_XXXX";
   char tag[sizeof(big_payload_tag)];
@@ -1045,18 +1208,32 @@
   snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
 
   std::string longString(3266519, 'x');
-  ssize_t ret;
 
-  auto write_function = [&] {
-    ret = LOG_FAILURE_RETRY(
-        __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, longString.c_str()));
-  };
+  ssize_t ret = LOG_FAILURE_RETRY(__android_log_buf_write(
+      LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, longString.c_str()));
 
-  auto check_function = [&](log_msg log_msg, bool* found) {
+  struct logger_list* logger_list;
+
+  ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+                           LOG_ID_SYSTEM,
+                           ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 100, 0)));
+
+  ssize_t max_len = 0;
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
+    }
+
+    if ((log_msg.entry.pid != pid) || (log_msg.id() != LOG_ID_SYSTEM)) {
+      continue;
+    }
+
     char* data = log_msg.msg();
 
     if (!data || strcmp(++data, tag)) {
-      return;
+      continue;
     }
 
     data += strlen(data) + 1;
@@ -1068,38 +1245,37 @@
       ++right;
     }
 
-    ssize_t len = left - data + 1;
-    // Check that we don't see any entries larger than the max payload.
-    EXPECT_LE(static_cast<size_t>(len), LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag));
-
-    // Once we've found our expected entry, break.
-    if (len == LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag)) {
-      EXPECT_EQ(ret, len + static_cast<ssize_t>(sizeof(big_payload_tag)));
-      *found = true;
+    if (max_len <= (left - data)) {
+      max_len = left - data + 1;
     }
-  };
+  }
 
-  RunLogTests(LOG_ID_SYSTEM, write_function, check_function);
+  android_logger_list_close(logger_list);
 
+#if !SUPPORTS_END_TO_END
+  max_len =
+      max_len ? max_len : LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag);
+#endif
+  EXPECT_LE(LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag),
+            static_cast<size_t>(max_len));
+
+  // SLOP: Allow the underlying interface to optionally place a
+  // terminating nul at the LOGGER_ENTRY_MAX_PAYLOAD's last byte
+  // or not.
+  if (ret == (max_len + static_cast<ssize_t>(sizeof(big_payload_tag)) - 1)) {
+    --max_len;
+  }
+  EXPECT_EQ(ret, max_len + static_cast<ssize_t>(sizeof(big_payload_tag)));
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 TEST(liblog, dual_reader) {
-#ifdef __ANDROID__
-  static const int expected_count1 = 25;
-  static const int expected_count2 = 25;
+#ifdef TEST_PREFIX
+  TEST_PREFIX
 
-  pid_t pid = getpid();
-
-  auto logger_list1 = std::unique_ptr<struct logger_list, ListCloser>{
-      android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY, expected_count1, pid)};
-  ASSERT_TRUE(logger_list1);
-
-  auto logger_list2 = std::unique_ptr<struct logger_list, ListCloser>{
-      android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY, expected_count2, pid)};
-  ASSERT_TRUE(logger_list2);
+  static const int num = 25;
 
   for (int i = 25; i > 0; --i) {
     static const char fmt[] = "dual_reader %02d";
@@ -1108,46 +1284,32 @@
     LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                               "liblog", buffer));
   }
+  usleep(1000000);
 
-  alarm(2);
-  auto alarm_guard = android::base::make_scope_guard([] { alarm(0); });
+  struct logger_list* logger_list1;
+  ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open(
+                           LOG_ID_MAIN,
+                           ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, num, 0)));
 
-  // Wait until we see all messages with the blocking reader.
-  int count1 = 0;
-  int count2 = 0;
+  struct logger_list* logger_list2;
 
-  while (count1 != expected_count2 || count2 != expected_count2) {
-    log_msg log_msg;
-    if (count1 < expected_count1) {
-      ASSERT_GT(android_logger_list_read(logger_list1.get(), &log_msg), 0);
-      count1++;
-    }
-    if (count2 < expected_count2) {
-      ASSERT_GT(android_logger_list_read(logger_list2.get(), &log_msg), 0);
-      count2++;
-    }
+  if (NULL == (logger_list2 = android_logger_list_open(
+                   LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                   num - 10, 0))) {
+    android_logger_list_close(logger_list1);
+    ASSERT_TRUE(NULL != logger_list2);
   }
 
-  // Test again with the nonblocking reader.
-  auto logger_list_non_block1 =
-      std::unique_ptr<struct logger_list, ListCloser>{android_logger_list_open(
-          LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, expected_count1, pid)};
-  ASSERT_TRUE(logger_list_non_block1);
-
-  auto logger_list_non_block2 =
-      std::unique_ptr<struct logger_list, ListCloser>{android_logger_list_open(
-          LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, expected_count2, pid)};
-  ASSERT_TRUE(logger_list_non_block2);
-  count1 = 0;
-  count2 = 0;
+  int count1 = 0;
   bool done1 = false;
+  int count2 = 0;
   bool done2 = false;
 
-  while (!done1 || !done2) {
+  do {
     log_msg log_msg;
 
     if (!done1) {
-      if (android_logger_list_read(logger_list_non_block1.get(), &log_msg) <= 0) {
+      if (android_logger_list_read(logger_list1, &log_msg) <= 0) {
         done1 = true;
       } else {
         ++count1;
@@ -1155,21 +1317,25 @@
     }
 
     if (!done2) {
-      if (android_logger_list_read(logger_list_non_block2.get(), &log_msg) <= 0) {
+      if (android_logger_list_read(logger_list2, &log_msg) <= 0) {
         done2 = true;
       } else {
         ++count2;
       }
     }
-  }
+  } while ((!done1) || (!done2));
 
-  EXPECT_EQ(expected_count1, count1);
-  EXPECT_EQ(expected_count2, count2);
+  android_logger_list_close(logger_list1);
+  android_logger_list_close(logger_list2);
+
+  EXPECT_EQ(num * SUPPORTS_END_TO_END, count1);
+  EXPECT_EQ((num - 10) * SUPPORTS_END_TO_END, count2);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
+#ifdef USING_LOGGER_DEFAULT  // Do not retest logprint
 static bool checkPriForTag(AndroidLogFormat* p_format, const char* tag,
                            android_LogPriority pri) {
   return android_log_shouldPrintLine(p_format, tag, pri) &&
@@ -1245,8 +1411,9 @@
 
   android_log_format_free(p_format);
 }
+#endif  // USING_LOGGER_DEFAULT
 
-#ifdef ENABLE_FLAKY_TESTS
+#ifdef USING_LOGGER_DEFAULT  // Do not retest property handling
 TEST(liblog, is_loggable) {
 #ifdef __ANDROID__
   static const char tag[] = "is_loggable";
@@ -1534,13 +1701,14 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // ENABLE_FLAKY_TESTS
+#endif  // USING_LOGGER_DEFAULT
 
-#ifdef ENABLE_FLAKY_TESTS
 // Following tests the specific issues surrounding error handling wrt logd.
 // Kills logd and toss all collected data, equivalent to logcat -b all -c,
 // except we also return errors to the logging callers.
+#ifdef USING_LOGGER_DEFAULT
 #ifdef __ANDROID__
+#ifdef TEST_PREFIX
 // helper to liblog.enoent to count end-to-end matching logging messages.
 static int count_matching_ts(log_time ts) {
   usleep(1000000);
@@ -1576,18 +1744,21 @@
   return count;
 }
 
-TEST(liblog, enoent) {
-#ifdef __ANDROID__
-  if (getuid() != 0) {
-    GTEST_SKIP() << "Skipping test, must be run as root.";
-    return;
-  }
+// meant to be handed to ASSERT_TRUE / EXPECT_TRUE only to expand the message
+static testing::AssertionResult IsOk(bool ok, std::string& message) {
+  return ok ? testing::AssertionSuccess()
+            : (testing::AssertionFailure() << message);
+}
+#endif  // TEST_PREFIX
 
+TEST(liblog, enoent) {
+#ifdef TEST_PREFIX
+  TEST_PREFIX
   log_time ts(CLOCK_MONOTONIC);
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
-  EXPECT_EQ(1, count_matching_ts(ts));
+  EXPECT_EQ(SUPPORTS_END_TO_END, count_matching_ts(ts));
 
-  // This call will fail unless we are root, beware of any
+  // This call will fail if we are setuid(AID_SYSTEM), beware of any
   // test prior to this one playing with setuid and causing interference.
   // We need to run before these tests so that they do not interfere with
   // this test.
@@ -1599,7 +1770,20 @@
   // liblog.android_logger_get_ is one of those tests that has no recourse
   // and that would be adversely affected by emptying the log if it was run
   // right after this test.
-  system("stop logd");
+  if (getuid() != AID_ROOT) {
+    fprintf(
+        stderr,
+        "WARNING: test conditions request being run as root and not AID=%d\n",
+        getuid());
+    if (!__android_log_is_debuggable()) {
+      fprintf(
+          stderr,
+          "WARNING: can not run test on a \"user\" build, bypassing test\n");
+      return;
+    }
+  }
+
+  system((getuid() == AID_ROOT) ? "stop logd" : "su 0 stop logd");
   usleep(1000000);
 
   // A clean stop like we are testing returns -ENOENT, but in the _real_
@@ -1611,34 +1795,38 @@
   std::string content = android::base::StringPrintf(
       "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
       ret, (ret <= 0) ? strerror(-ret) : "(content sent)");
-  EXPECT_TRUE(ret == -ENOENT || ret == -ENOTCONN || ret == -ECONNREFUSED) << content;
+  EXPECT_TRUE(
+      IsOk((ret == -ENOENT) || (ret == -ENOTCONN) || (ret == -ECONNREFUSED),
+           content));
   ret = __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts));
   content = android::base::StringPrintf(
       "__android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)) = %d %s\n",
       ret, (ret <= 0) ? strerror(-ret) : "(content sent)");
-  EXPECT_TRUE(ret == -ENOENT || ret == -ENOTCONN || ret == -ECONNREFUSED) << content;
+  EXPECT_TRUE(
+      IsOk((ret == -ENOENT) || (ret == -ENOTCONN) || (ret == -ECONNREFUSED),
+           content));
   EXPECT_EQ(0, count_matching_ts(ts));
 
-  system("start logd");
+  system((getuid() == AID_ROOT) ? "start logd" : "su 0 start logd");
   usleep(1000000);
 
   EXPECT_EQ(0, count_matching_ts(ts));
 
   ts = log_time(CLOCK_MONOTONIC);
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
-  EXPECT_EQ(1, count_matching_ts(ts));
+  EXPECT_EQ(SUPPORTS_END_TO_END, count_matching_ts(ts));
 
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 #endif  // __ANDROID__
-#endif  // ENABLE_FLAKY_TESTS
+#endif  // USING_LOGGER_DEFAULT
 
 // Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
 
-#ifdef ENABLE_FLAKY_TESTS
 // Do not retest properties, and cannot log into LOG_ID_SECURITY
+#ifdef USING_LOGGER_DEFAULT
 TEST(liblog, __security) {
 #ifdef __ANDROID__
   static const char persist_key[] = "persist.logd.security";
@@ -1895,74 +2083,113 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // ENABLE_FLAKY_TESTS
+#endif  // USING_LOGGER_DEFAULT
 
-#ifdef __ANDROID__
-static void android_errorWriteWithInfoLog_helper(int tag, const char* subtag, int uid,
-                                                 const char* payload, int data_len) {
-  auto write_function = [&] {
-    int ret = android_errorWriteWithInfoLog(tag, subtag, uid, payload, data_len);
-    ASSERT_LT(0, ret);
-  };
+#ifdef TEST_PREFIX
+static void android_errorWriteWithInfoLog_helper(int TAG, const char* SUBTAG,
+                                                 int UID, const char* payload,
+                                                 int DATA_LEN, int& count) {
+  TEST_PREFIX
+  struct logger_list* logger_list;
 
-  auto check_function = [&](log_msg log_msg, bool* found) {
-    char* event_data = log_msg.msg();
-    char* original = event_data;
+  pid_t pid = getpid();
+
+  count = 0;
+
+  ASSERT_TRUE(NULL !=
+              (logger_list = android_logger_list_open(
+                   LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                   1000, pid)));
+
+  int retval_android_errorWriteWithinInfoLog =
+      android_errorWriteWithInfoLog(TAG, SUBTAG, UID, payload, DATA_LEN);
+  if (payload) {
+    ASSERT_LT(0, retval_android_errorWriteWithinInfoLog);
+  } else {
+    ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
+  }
+
+  sleep(2);
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
+    }
+
+    char* eventData = log_msg.msg();
+    if (!eventData) {
+      continue;
+    }
+
+    char* original = eventData;
 
     // Tag
-    auto* event_header = reinterpret_cast<android_event_header_t*>(event_data);
-    event_data += sizeof(android_event_header_t);
-    if (event_header->tag != tag) {
-      return;
+    int tag = get4LE(eventData);
+    eventData += 4;
+
+    if (tag != TAG) {
+      continue;
+    }
+
+    if (!payload) {
+      // This tag should not have been written because the data was null
+      ++count;
+      break;
     }
 
     // List type
-    auto* event_list = reinterpret_cast<android_event_list_t*>(event_data);
-    ASSERT_EQ(EVENT_TYPE_LIST, event_list->type);
-    ASSERT_EQ(3, event_list->element_count);
-    event_data += sizeof(android_event_list_t);
+    ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+    eventData++;
+
+    // Number of elements in list
+    ASSERT_EQ(3, eventData[0]);
+    eventData++;
 
     // Element #1: string type for subtag
-    auto* event_string_subtag = reinterpret_cast<android_event_string_t*>(event_data);
-    ASSERT_EQ(EVENT_TYPE_STRING, event_string_subtag->type);
-    int32_t subtag_len = strlen(subtag);
-    if (subtag_len > 32) {
-      subtag_len = 32;
+    ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+    eventData++;
+
+    unsigned subtag_len = strlen(SUBTAG);
+    if (subtag_len > 32) subtag_len = 32;
+    ASSERT_EQ(subtag_len, get4LE(eventData));
+    eventData += 4;
+
+    if (memcmp(SUBTAG, eventData, subtag_len)) {
+      continue;
     }
-    ASSERT_EQ(subtag_len, event_string_subtag->length);
-    if (memcmp(subtag, &event_string_subtag->data, subtag_len)) {
-      return;
-    }
-    event_data += sizeof(android_event_string_t) + subtag_len;
+    eventData += subtag_len;
 
     // Element #2: int type for uid
-    auto* event_int_uid = reinterpret_cast<android_event_int_t*>(event_data);
-    ASSERT_EQ(EVENT_TYPE_INT, event_int_uid->type);
-    ASSERT_EQ(uid, event_int_uid->data);
-    event_data += sizeof(android_event_int_t);
+    ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
+    eventData++;
+
+    ASSERT_EQ(UID, (int)get4LE(eventData));
+    eventData += 4;
 
     // Element #3: string type for data
-    auto* event_string_data = reinterpret_cast<android_event_string_t*>(event_data);
-    ASSERT_EQ(EVENT_TYPE_STRING, event_string_data->type);
-    int32_t message_data_len = event_string_data->length;
-    if (data_len < 512) {
-      ASSERT_EQ(data_len, message_data_len);
-    }
-    if (memcmp(payload, &event_string_data->data, message_data_len) != 0) {
-      return;
-    }
-    event_data += sizeof(android_event_string_t);
+    ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+    eventData++;
 
-    if (data_len >= 512) {
-      event_data += message_data_len;
+    size_t dataLen = get4LE(eventData);
+    eventData += 4;
+    if (DATA_LEN < 512) ASSERT_EQ(DATA_LEN, (int)dataLen);
+
+    if (memcmp(payload, eventData, dataLen)) {
+      continue;
+    }
+
+    if (DATA_LEN >= 512) {
+      eventData += dataLen;
       // 4 bytes for the tag, and max_payload_buf should be truncated.
-      ASSERT_LE(4 + 512, event_data - original);       // worst expectations
-      ASSERT_GT(4 + data_len, event_data - original);  // must be truncated
+      ASSERT_LE(4 + 512, eventData - original);       // worst expectations
+      ASSERT_GT(4 + DATA_LEN, eventData - original);  // must be truncated
     }
-    *found = true;
-  };
 
-  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+    ++count;
+  }
+
+  android_logger_list_close(logger_list);
 }
 #endif
 
@@ -1976,8 +2203,11 @@
 #endif
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
-#ifdef __ANDROID__
-  android_errorWriteWithInfoLog_helper(UNIQUE_TAG(1), "test-subtag", -1, max_payload_buf, 200);
+#ifdef TEST_PREFIX
+  int count;
+  android_errorWriteWithInfoLog_helper(UNIQUE_TAG(1), "test-subtag", -1,
+                                       max_payload_buf, 200, count);
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -1985,21 +2215,24 @@
 
 TEST(liblog,
      android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
-#ifdef __ANDROID__
-  android_errorWriteWithInfoLog_helper(UNIQUE_TAG(2), "test-subtag", -1, max_payload_buf,
-                                       sizeof(max_payload_buf));
+#ifdef TEST_PREFIX
+  int count;
+  android_errorWriteWithInfoLog_helper(UNIQUE_TAG(2), "test-subtag", -1,
+                                       max_payload_buf, sizeof(max_payload_buf),
+                                       count);
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
-// TODO: Do we need to check that we didn't actually write anything if we return a failure here?
 TEST(liblog,
      android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
-#ifdef __ANDROID__
-  int retval_android_errorWriteWithinInfoLog =
-      android_errorWriteWithInfoLog(UNIQUE_TAG(3), "test-subtag", -1, nullptr, 200);
-  ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
+#ifdef TEST_PREFIX
+  int count;
+  android_errorWriteWithInfoLog_helper(UNIQUE_TAG(3), "test-subtag", -1, NULL,
+                                       200, count);
+  EXPECT_EQ(0, count);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2007,9 +2240,12 @@
 
 TEST(liblog,
      android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+  int count;
   android_errorWriteWithInfoLog_helper(
-      UNIQUE_TAG(4), "abcdefghijklmnopqrstuvwxyz now i know my abc", -1, max_payload_buf, 200);
+      UNIQUE_TAG(4), "abcdefghijklmnopqrstuvwxyz now i know my abc", -1,
+      max_payload_buf, 200, count);
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2023,59 +2259,144 @@
   buf_write_test(max_payload_buf);
 }
 
-TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
-#ifdef __ANDROID__
-  int kTag = UNIQUE_TAG(5);
-  const char* kSubTag = "test-subtag";
+#ifdef TEST_PREFIX
+static void android_errorWriteLog_helper(int TAG, const char* SUBTAG,
+                                         int& count) {
+  TEST_PREFIX
+  struct logger_list* logger_list;
 
-  auto write_function = [&] {
-    int retval_android_errorWriteLog = android_errorWriteLog(kTag, kSubTag);
-    ASSERT_LT(0, retval_android_errorWriteLog);
-  };
+  pid_t pid = getpid();
 
-  auto check_function = [&](log_msg log_msg, bool* found) {
-    char* event_data = log_msg.msg();
+  count = 0;
+
+  // Do a Before and After on the count to measure the effect. Decrement
+  // what we find in Before to set the stage.
+  ASSERT_TRUE(NULL !=
+              (logger_list = android_logger_list_open(
+                   LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                   1000, pid)));
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+    char* eventData = log_msg.msg();
+    if (!eventData) continue;
 
     // Tag
-    auto* event_header = reinterpret_cast<android_event_header_t*>(event_data);
-    event_data += sizeof(android_event_header_t);
-    if (event_header->tag != kTag) {
-      return;
+    int tag = get4LE(eventData);
+    eventData += 4;
+
+    if (tag != TAG) continue;
+
+    if (!SUBTAG) {
+      // This tag should not have been written because the data was null
+      --count;
+      break;
     }
 
     // List type
-    auto* event_list = reinterpret_cast<android_event_list_t*>(event_data);
-    ASSERT_EQ(EVENT_TYPE_LIST, event_list->type);
-    ASSERT_EQ(3, event_list->element_count);
-    event_data += sizeof(android_event_list_t);
+    eventData++;
+    // Number of elements in list
+    eventData++;
+    // Element #1: string type for subtag
+    eventData++;
+
+    eventData += 4;
+
+    if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) continue;
+    --count;
+  }
+
+  android_logger_list_close(logger_list);
+
+  // Do an After on the count to measure the effect.
+  ASSERT_TRUE(NULL !=
+              (logger_list = android_logger_list_open(
+                   LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                   1000, pid)));
+
+  int retval_android_errorWriteLog = android_errorWriteLog(TAG, SUBTAG);
+  if (SUBTAG) {
+    ASSERT_LT(0, retval_android_errorWriteLog);
+  } else {
+    ASSERT_GT(0, retval_android_errorWriteLog);
+  }
+
+  sleep(2);
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
+    }
+
+    char* eventData = log_msg.msg();
+    if (!eventData) {
+      continue;
+    }
+
+    // Tag
+    int tag = get4LE(eventData);
+    eventData += 4;
+
+    if (tag != TAG) {
+      continue;
+    }
+
+    if (!SUBTAG) {
+      // This tag should not have been written because the data was null
+      ++count;
+      break;
+    }
+
+    // List type
+    ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
+    eventData++;
+
+    // Number of elements in list
+    ASSERT_EQ(3, eventData[0]);
+    eventData++;
 
     // Element #1: string type for subtag
-    auto* event_string_subtag = reinterpret_cast<android_event_string_t*>(event_data);
-    ASSERT_EQ(EVENT_TYPE_STRING, event_string_subtag->type);
-    int32_t subtag_len = strlen(kSubTag);
-    ASSERT_EQ(subtag_len, event_string_subtag->length);
-    if (memcmp(kSubTag, &event_string_subtag->data, subtag_len) == 0) {
-      *found = true;
+    ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
+    eventData++;
+
+    ASSERT_EQ(strlen(SUBTAG), get4LE(eventData));
+    eventData += 4;
+
+    if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+      continue;
     }
-  };
+    ++count;
+  }
 
-  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+  android_logger_list_close(logger_list);
+}
+#endif
 
+TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
+#ifdef TEST_PREFIX
+  int count;
+  android_errorWriteLog_helper(UNIQUE_TAG(5), "test-subtag", count);
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
-#ifdef __ANDROID__
-  EXPECT_LT(android_errorWriteLog(UNIQUE_TAG(6), nullptr), 0);
+#ifdef TEST_PREFIX
+  int count;
+  android_errorWriteLog_helper(UNIQUE_TAG(6), NULL, count);
+  EXPECT_EQ(0, count);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 // Do not retest logger list handling
-#ifdef __ANDROID__
+#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));
@@ -2230,9 +2551,9 @@
 
   return 0;
 }
-#endif  // __ANDROID__
+#endif  // TEST_PREFIX
 
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
 static const char* event_test_int32(uint32_t tag, size_t& expected_len) {
   android_log_context ctx;
 
@@ -2486,27 +2807,59 @@
 
 static void create_android_logger(const char* (*fn)(uint32_t tag,
                                                     size_t& expected_len)) {
-  size_t expected_len;
-  const char* expected_string;
-  auto write_function = [&] {
-    expected_string = (*fn)(1005, expected_len);
-    ASSERT_NE(nullptr, expected_string);
-  };
+  TEST_PREFIX
+  struct logger_list* logger_list;
 
   pid_t pid = getpid();
-  auto check_function = [&](log_msg log_msg, bool* found) {
-    if (static_cast<size_t>(log_msg.entry.len) != expected_len) {
-      return;
+
+  ASSERT_TRUE(NULL !=
+              (logger_list = android_logger_list_open(
+                   LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK,
+                   1000, pid)));
+
+#ifdef __ANDROID__
+  log_time ts(android_log_clockid());
+#else
+  log_time ts(CLOCK_REALTIME);
+#endif
+
+  size_t expected_len;
+  const char* expected_string = (*fn)(1005, expected_len);
+
+  if (!expected_string) {
+    android_logger_list_close(logger_list);
+    return;
+  }
+
+  usleep(1000000);
+
+  int count = 0;
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+      break;
+    }
+
+    ASSERT_EQ(log_msg.entry.pid, pid);
+
+    if ((log_msg.entry.sec < (ts.tv_sec - 1)) ||
+        ((ts.tv_sec + 1) < log_msg.entry.sec) ||
+        ((size_t)log_msg.entry.len != expected_len) ||
+        (log_msg.id() != LOG_ID_EVENTS)) {
+      continue;
     }
 
     char* eventData = log_msg.msg();
 
+    ++count;
+
     AndroidLogFormat* logformat = android_log_format_new();
     EXPECT_TRUE(NULL != logformat);
     AndroidLogEntry entry;
     char msgBuf[1024];
-    int processBinaryLogBuffer =
-        android_log_processBinaryLogBuffer(&log_msg.entry, &entry, nullptr, msgBuf, sizeof(msgBuf));
+    int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+        &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
     EXPECT_EQ(0, processBinaryLogBuffer);
     if (processBinaryLogBuffer == 0) {
       int line_overhead = 20;
@@ -2523,28 +2876,29 @@
     // test buffer reading API
     int buffer_to_string = -1;
     if (eventData) {
-      auto* event_header = reinterpret_cast<android_event_header_t*>(eventData);
-      eventData += sizeof(android_event_header_t);
-      snprintf(msgBuf, sizeof(msgBuf), "I/[%" PRId32 "]", event_header->tag);
+      snprintf(msgBuf, sizeof(msgBuf), "I/[%" PRIu32 "]", get4LE(eventData));
       print_barrier();
       fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
       memset(msgBuf, 0, sizeof(msgBuf));
-      buffer_to_string =
-          android_log_buffer_to_string(eventData, log_msg.entry.len, msgBuf, sizeof(msgBuf));
+      buffer_to_string = android_log_buffer_to_string(
+          eventData + sizeof(uint32_t), log_msg.entry.len - sizeof(uint32_t),
+          msgBuf, sizeof(msgBuf));
       fprintf(stderr, "%s\n", msgBuf);
       print_barrier();
     }
     EXPECT_EQ(0, buffer_to_string);
-    EXPECT_STREQ(expected_string, msgBuf);
-    *found = true;
-  };
+    EXPECT_EQ(strlen(expected_string), strlen(msgBuf));
+    EXPECT_EQ(0, strcmp(expected_string, msgBuf));
+  }
 
-  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+  EXPECT_EQ(SUPPORTS_END_TO_END, count);
+
+  android_logger_list_close(logger_list);
 }
 #endif
 
 TEST(liblog, create_android_logger_int32) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_int32);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2552,7 +2906,7 @@
 }
 
 TEST(liblog, create_android_logger_int64) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_int64);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2560,7 +2914,7 @@
 }
 
 TEST(liblog, create_android_logger_list_int64) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_list_int64);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2568,7 +2922,7 @@
 }
 
 TEST(liblog, create_android_logger_simple_automagic_list) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_simple_automagic_list);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2576,7 +2930,7 @@
 }
 
 TEST(liblog, create_android_logger_list_empty) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_list_empty);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2584,7 +2938,7 @@
 }
 
 TEST(liblog, create_android_logger_complex_nested_list) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_complex_nested_list);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2592,7 +2946,7 @@
 }
 
 TEST(liblog, create_android_logger_7_level_prefix) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_7_level_prefix);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2600,7 +2954,7 @@
 }
 
 TEST(liblog, create_android_logger_7_level_suffix) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_7_level_suffix);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2608,7 +2962,7 @@
 }
 
 TEST(liblog, create_android_logger_android_log_error_write) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_android_log_error_write);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2616,13 +2970,14 @@
 }
 
 TEST(liblog, create_android_logger_android_log_error_write_null) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
   create_android_logger(event_test_android_log_error_write_null);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
+#ifdef USING_LOGGER_DEFAULT  // Do not retest logger list handling
 TEST(liblog, create_android_logger_overflow) {
   android_log_context ctx;
 
@@ -2650,7 +3005,23 @@
   ASSERT_TRUE(NULL == ctx);
 }
 
-#ifdef ENABLE_FLAKY_TESTS
+TEST(liblog, android_log_write_list_buffer) {
+  __android_log_event_list ctx(1005);
+  ctx << 1005 << "tag_def"
+      << "(tag|1),(name|3),(format|3)";
+  std::string buffer(ctx);
+  ctx.close();
+
+  char msgBuf[1024];
+  memset(msgBuf, 0, sizeof(msgBuf));
+  EXPECT_EQ(android_log_buffer_to_string(buffer.data(), buffer.length(), msgBuf,
+                                         sizeof(msgBuf)),
+            0);
+  EXPECT_STREQ(msgBuf, "[1005,tag_def,(tag|1),(name|3),(format|3)]");
+}
+#endif  // USING_LOGGER_DEFAULT
+
+#ifdef USING_LOGGER_DEFAULT  // Do not retest pmsg functionality
 #ifdef __ANDROID__
 #ifndef NO_PSTORE
 static const char __pmsg_file[] =
@@ -2728,7 +3099,7 @@
   EXPECT_EQ(ANDROID_LOG_VERBOSE, prio);
   EXPECT_FALSE(NULL == strstr(__pmsg_file, filename));
   EXPECT_EQ(len, sizeof(max_payload_buf));
-  EXPECT_STREQ(max_payload_buf, buf);
+  EXPECT_EQ(0, strcmp(max_payload_buf, buf));
 
   ++signaled;
   if ((len != sizeof(max_payload_buf)) || strcmp(max_payload_buf, buf)) {
@@ -2787,8 +3158,9 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // ENABLE_FLAKY_TESTS
+#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);
@@ -2805,3 +3177,4 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+#endif  // USING_LOGGER_DEFAULT
diff --git a/liblog/tests/liblog_test_default.cpp b/liblog/tests/liblog_test_default.cpp
new file mode 100644
index 0000000..9fc443c
--- /dev/null
+++ b/liblog/tests/liblog_test_default.cpp
@@ -0,0 +1,5 @@
+#ifdef __ANDROID__
+#include <log/log_transport.h>
+#define TEST_LOGGER LOGGER_DEFAULT
+#endif
+#include "liblog_test.cpp"
diff --git a/liblog/tests/liblog_test_stderr.cpp b/liblog/tests/liblog_test_stderr.cpp
new file mode 100644
index 0000000..abc1b9c
--- /dev/null
+++ b/liblog/tests/liblog_test_stderr.cpp
@@ -0,0 +1,5 @@
+#include <log/log_transport.h>
+#define liblog liblog_stderr
+#define TEST_LOGGER LOGGER_STDERR
+#define USING_LOGGER_STDERR
+#include "liblog_test.cpp"
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 1be99aa..443c3ea 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -29,6 +29,54 @@
 // Do not use anything in log/log_time.h despite side effects of the above.
 #include <private/android_logger.h>
 
+TEST(liblog, __android_log_write__android_logger_list_read) {
+#ifdef __ANDROID__
+  pid_t pid = getpid();
+
+  struct logger_list* logger_list;
+  ASSERT_TRUE(
+      NULL !=
+      (logger_list = android_logger_list_open(
+           LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  std::string buf = android::base::StringPrintf("pid=%u ts=%ld.%09ld", pid,
+                                                ts.tv_sec, ts.tv_nsec);
+  static const char tag[] =
+      "liblog.__android_log_write__android_logger_list_read";
+  static const char prio = ANDROID_LOG_DEBUG;
+  ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str()));
+  usleep(1000000);
+
+  buf = std::string(&prio, sizeof(prio)) + tag + std::string("", 1) + buf +
+        std::string("", 1);
+
+  int count = 0;
+
+  for (;;) {
+    log_msg log_msg;
+    if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+    EXPECT_EQ(log_msg.entry.pid, pid);
+    // There may be a future where we leak "liblog" tagged LOG_ID_EVENT
+    // binary messages through so that logger losses can be correlated?
+    EXPECT_EQ(log_msg.id(), LOG_ID_MAIN);
+
+    if (log_msg.entry.len != buf.length()) continue;
+
+    if (buf != std::string(log_msg.msg(), log_msg.entry.len)) continue;
+
+    ++count;
+  }
+  android_logger_list_close(logger_list);
+
+  EXPECT_EQ(1, count);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
 TEST(liblog, android_logger_get_) {
 #ifdef __ANDROID__
   // This test assumes the log buffers are filled with noise from
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
index e06964f..ebf0b15 100644
--- a/liblog/tests/log_wrap_test.cpp
+++ b/liblog/tests/log_wrap_test.cpp
@@ -27,9 +27,12 @@
 #include <log/log_properties.h>
 #include <log/log_read.h>
 #include <log/log_time.h>
+#include <log/log_transport.h>
 
 #ifdef __ANDROID__
 static void read_with_wrap() {
+  android_set_log_transport(LOGGER_LOGD);
+
   // Read the last line in the log to get a starting timestamp. We're assuming
   // the log is not empty.
   const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
@@ -58,27 +61,60 @@
 
   android_logger_list_close(logger_list);
 }
+
+static void caught_signal(int /* signum */) {
+}
 #endif
 
 // b/64143705 confirm fixed
 TEST(liblog, wrap_mode_blocks) {
 #ifdef __ANDROID__
-  // The read call is expected to take up to 2 hours in the happy case.  There was a previous bug
-  // where it would take only 30 seconds due to an alarm() in logd_reader.cpp.  That alarm has been
-  // removed, so we check here that the read call blocks for a reasonable amount of time (5s).
-
-  struct sigaction ignore = {.sa_handler = [](int) { _exit(0); }};
-  struct sigaction old_sigaction;
-  sigaction(SIGALRM, &ignore, &old_sigaction);
-  alarm(5);
 
   android::base::Timer timer;
-  read_with_wrap();
 
-  FAIL() << "read_with_wrap() should not return before the alarm is triggered.";
+  // The read call is expected to take up to 2 hours in the happy case.
+  // We only want to make sure it waits for longer than 30s, but we can't
+  // use an alarm as the implementation uses it. So we run the test in
+  // a separate process.
+  pid_t pid = fork();
+
+  if (pid == 0) {
+    // child
+    read_with_wrap();
+    _exit(0);
+  }
+
+  struct sigaction ignore, old_sigaction;
+  memset(&ignore, 0, sizeof(ignore));
+  ignore.sa_handler = caught_signal;
+  sigemptyset(&ignore.sa_mask);
+  sigaction(SIGALRM, &ignore, &old_sigaction);
+  alarm(45);
+
+  bool killed = false;
+  for (;;) {
+    siginfo_t info = {};
+    // This wait will succeed if the child exits, or fail with EINTR if the
+    // alarm goes off first - a loose approximation to a timed wait.
+    int ret = waitid(P_PID, pid, &info, WEXITED);
+    if (ret >= 0 || errno != EINTR) {
+      EXPECT_EQ(ret, 0);
+      if (!killed) {
+        EXPECT_EQ(info.si_status, 0);
+      }
+      break;
+    }
+    unsigned int alarm_left = alarm(0);
+    if (alarm_left > 0) {
+      alarm(alarm_left);
+    } else {
+      kill(pid, SIGTERM);
+      killed = true;
+    }
+  }
 
   alarm(0);
-  sigaction(SIGALRM, &old_sigaction, nullptr);
+  EXPECT_GT(timer.duration(), std::chrono::seconds(40));
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/liblog/tests/logprint_test.cpp b/liblog/tests/logprint_test.cpp
deleted file mode 100644
index 7ca02ac..0000000
--- a/liblog/tests/logprint_test.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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/libmeminfo/Android.bp b/libmeminfo/Android.bp
index 8dcc77b..fc022bd 100644
--- a/libmeminfo/Android.bp
+++ b/libmeminfo/Android.bp
@@ -26,17 +26,10 @@
         "liblog",
         "libprocinfo",
     ],
-    target: {
-        darwin: {
-            enabled: false,
-        },
-
-    },
 }
 
 cc_library {
     name: "libmeminfo",
-    host_supported: true,
     defaults: ["libmeminfo_defaults"],
     export_include_dirs: ["include"],
     export_shared_lib_headers: ["libbase"],
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index 8c1280f..1fb4151 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -45,13 +45,6 @@
     // vector.
     const std::vector<Vma>& MapsWithPageIdle();
 
-    // Same as Maps() except, do not read the usage stats for each map.
-    const std::vector<Vma>& MapsWithoutUsageStats();
-
-    // If MapsWithoutUsageStats was called, this function will fill in
-    // usage stats for this single vma.
-    bool FillInVmaStats(Vma& vma);
-
     // 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.
     //
@@ -95,7 +88,7 @@
     ~ProcMemInfo() = default;
 
   private:
-    bool ReadMaps(bool get_wss, bool use_pageidle = false, bool get_usage_stats = true);
+    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_;
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
index 7bba599..eb53e57 100644
--- a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
+++ b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
@@ -17,7 +17,6 @@
 #include <inttypes.h>
 #include <linux/dma-buf.h>
 #include <poll.h>
-#include <string.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -231,7 +230,7 @@
     DmaBufTester() : ion_fd(ion_open()), ion_heap_mask(get_ion_heap_mask()) {}
 
     ~DmaBufTester() {
-        if (ion_fd >= 0) {
+        if (is_valid()) {
             ion_close(ion_fd);
         }
     }
@@ -242,16 +241,12 @@
         int fd;
         int err = ion_alloc_fd(ion_fd, size, 0, ion_heap_mask, 0, &fd);
         if (err < 0) {
-            printf("Failed ion_alloc_fd, return value: %d\n", err);
-            return unique_fd{};
+            return unique_fd{err};
         }
 
         if (!name.empty()) {
-            if (ioctl(fd, DMA_BUF_SET_NAME, name.c_str()) == -1) {
-                printf("Failed ioctl(DMA_BUF_SET_NAME): %s\n", strerror(errno));
-                close(fd);
-                return unique_fd{};
-            }
+            err = ioctl(fd, DMA_BUF_SET_NAME, name.c_str());
+            if (err < 0) return unique_fd{-errno};
         }
 
         return unique_fd{fd};
@@ -311,7 +306,7 @@
         return ret;
     }
 
-    int ion_fd;
+    unique_fd ion_fd;
     const int ion_heap_mask;
 };
 
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 378a4cd..5451ca3 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -31,7 +30,6 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
-#include <android-base/strings.h>
 
 using namespace std;
 using namespace android::meminfo;
@@ -62,130 +60,6 @@
     EXPECT_FALSE(maps.empty());
 }
 
-TEST(ProcMemInfo, MapsUsageNotEmpty) {
-    ProcMemInfo proc_mem(pid);
-    const std::vector<Vma>& maps = proc_mem.Maps();
-    EXPECT_FALSE(maps.empty());
-    uint64_t total_pss = 0;
-    uint64_t total_rss = 0;
-    uint64_t total_uss = 0;
-    for (auto& map : maps) {
-        ASSERT_NE(0, map.usage.vss);
-        total_rss += map.usage.rss;
-        total_pss += map.usage.pss;
-        total_uss += map.usage.uss;
-    }
-
-    // Crude check that stats are actually being read.
-    EXPECT_NE(0, total_rss) << "RSS zero for all maps, that is not possible.";
-    EXPECT_NE(0, total_pss) << "PSS zero for all maps, that is not possible.";
-    EXPECT_NE(0, total_uss) << "USS zero for all maps, that is not possible.";
-}
-
-TEST(ProcMemInfo, MapsUsageEmpty) {
-    ProcMemInfo proc_mem(pid);
-    const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
-    EXPECT_FALSE(maps.empty());
-    // Verify that all usage stats are zero in every map.
-    for (auto& map : maps) {
-        ASSERT_EQ(0, map.usage.vss);
-        ASSERT_EQ(0, map.usage.rss);
-        ASSERT_EQ(0, map.usage.pss);
-        ASSERT_EQ(0, map.usage.uss);
-        ASSERT_EQ(0, map.usage.swap);
-        ASSERT_EQ(0, map.usage.swap_pss);
-        ASSERT_EQ(0, map.usage.private_clean);
-        ASSERT_EQ(0, map.usage.private_dirty);
-        ASSERT_EQ(0, map.usage.shared_clean);
-        ASSERT_EQ(0, map.usage.shared_dirty);
-    }
-}
-
-TEST(ProcMemInfo, MapsUsageFillInLater) {
-    ProcMemInfo proc_mem(pid);
-    const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
-    EXPECT_FALSE(maps.empty());
-    for (auto& map : maps) {
-        Vma update_map(map);
-        ASSERT_EQ(map.start, update_map.start);
-        ASSERT_EQ(map.end, update_map.end);
-        ASSERT_EQ(map.offset, update_map.offset);
-        ASSERT_EQ(map.flags, update_map.flags);
-        ASSERT_EQ(map.name, update_map.name);
-        ASSERT_EQ(0, update_map.usage.vss);
-        ASSERT_EQ(0, update_map.usage.rss);
-        ASSERT_EQ(0, update_map.usage.pss);
-        ASSERT_EQ(0, update_map.usage.uss);
-        ASSERT_EQ(0, update_map.usage.swap);
-        ASSERT_EQ(0, update_map.usage.swap_pss);
-        ASSERT_EQ(0, update_map.usage.private_clean);
-        ASSERT_EQ(0, update_map.usage.private_dirty);
-        ASSERT_EQ(0, update_map.usage.shared_clean);
-        ASSERT_EQ(0, update_map.usage.shared_dirty);
-        ASSERT_TRUE(proc_mem.FillInVmaStats(update_map));
-        // Check that at least one usage stat was updated.
-        ASSERT_NE(0, update_map.usage.vss);
-    }
-}
-
-TEST(ProcMemInfo, PageMapPresent) {
-    static constexpr size_t kNumPages = 20;
-    size_t pagesize = getpagesize();
-    void* ptr = mmap(nullptr, pagesize * (kNumPages + 2), PROT_READ | PROT_WRITE,
-                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-    ASSERT_NE(MAP_FAILED, ptr);
-
-    // Unmap the first page and the last page so that we guarantee this
-    // map is in a map by itself.
-    ASSERT_EQ(0, munmap(ptr, pagesize));
-    uintptr_t addr = reinterpret_cast<uintptr_t>(ptr) + pagesize;
-    ASSERT_EQ(0, munmap(reinterpret_cast<void*>(addr + kNumPages * pagesize), pagesize));
-
-    ProcMemInfo proc_mem(getpid());
-    const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
-    ASSERT_FALSE(maps.empty());
-
-    // Find the vma associated with our previously created map.
-    const Vma* test_vma = nullptr;
-    for (const Vma& vma : maps) {
-        if (vma.start == addr) {
-            test_vma = &vma;
-            break;
-        }
-    }
-    ASSERT_TRUE(test_vma != nullptr) << "Cannot find test map.";
-
-    // Verify that none of the pages are listed as present.
-    std::vector<uint64_t> pagemap;
-    ASSERT_TRUE(proc_mem.PageMap(*test_vma, &pagemap));
-    ASSERT_EQ(kNumPages, pagemap.size());
-    for (size_t i = 0; i < pagemap.size(); i++) {
-        EXPECT_FALSE(android::meminfo::page_present(pagemap[i]))
-                << "Page " << i << " is present and it should not be.";
-    }
-
-    // Make some of the pages present and verify that we see them
-    // as present.
-    uint8_t* data = reinterpret_cast<uint8_t*>(addr);
-    data[0] = 1;
-    data[pagesize * 5] = 1;
-    data[pagesize * 11] = 1;
-
-    ASSERT_TRUE(proc_mem.PageMap(*test_vma, &pagemap));
-    ASSERT_EQ(kNumPages, pagemap.size());
-    for (size_t i = 0; i < pagemap.size(); i++) {
-        if (i == 0 || i == 5 || i == 11) {
-            EXPECT_TRUE(android::meminfo::page_present(pagemap[i]))
-                    << "Page " << i << " is not present and it should be.";
-        } else {
-            EXPECT_FALSE(android::meminfo::page_present(pagemap[i]))
-                    << "Page " << i << " is present and it should not be.";
-        }
-    }
-
-    ASSERT_EQ(0, munmap(reinterpret_cast<void*>(addr), kNumPages * pagesize));
-}
-
 TEST(ProcMemInfo, WssEmpty) {
     // If we created the object for getting usage,
     // the working set must be empty
@@ -362,9 +236,7 @@
     // 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_TRUE(vmas[2].name == "[anon:libc_malloc]" ||
-                android::base::StartsWith(vmas[2].name, "[anon:scudo:"))
-            << "Unknown map name " << vmas[2].name;
+    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]");
@@ -462,9 +334,7 @@
     // 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_TRUE(vmas[2].name == "[anon:libc_malloc]" ||
-                android::base::StartsWith(vmas[2].name, "[anon:scudo:"))
-            << "Unknown map name " << vmas[2].name;
+    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]");
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index 9e9a705..a8b43c1 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -130,14 +130,6 @@
     return maps_;
 }
 
-const std::vector<Vma>& ProcMemInfo::MapsWithoutUsageStats() {
-    if (maps_.empty() && !ReadMaps(get_wss_, false, false)) {
-        LOG(ERROR) << "Failed to read maps for Process " << pid_;
-    }
-
-    return maps_;
-}
-
 const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
     if (!maps_.empty()) {
         return maps_;
@@ -221,39 +213,29 @@
     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 == -1) {
+    if (pagemap_fd < 0) {
         PLOG(ERROR) << "Failed to open " << pagemap_file;
         return false;
     }
 
     uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
-    pagemap->resize(nr_pages);
+    pagemap->reserve(nr_pages);
 
-    size_t bytes_to_read = sizeof(uint64_t) * nr_pages;
-    off64_t start_addr = (vma.start / getpagesize()) * sizeof(uint64_t);
-    ssize_t bytes_read = pread64(pagemap_fd, pagemap->data(), bytes_to_read, start_addr);
-    if (bytes_read == -1) {
-        PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
-        return false;
-    } else if (static_cast<size_t>(bytes_read) != bytes_to_read) {
-        LOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_
-                   << ": read bytes " << bytes_read << " expected bytes " << bytes_to_read;
-        return false;
+    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;
 }
 
-static int GetPagemapFd(pid_t pid) {
-    std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid);
-    int fd = TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC));
-    if (fd == -1) {
-        PLOG(ERROR) << "Failed to open " << pagemap_file;
-    }
-    return fd;
-}
-
-bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle, bool get_usage_stats) {
+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).
@@ -274,12 +256,11 @@
         return false;
     }
 
-    if (!get_usage_stats) {
-        return true;
-    }
-
-    ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
-    if (pagemap_fd == -1) {
+    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;
     }
 
@@ -296,20 +277,6 @@
     return true;
 }
 
-bool ProcMemInfo::FillInVmaStats(Vma& vma) {
-    ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
-    if (pagemap_fd == -1) {
-        return false;
-    }
-
-    if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss_, false)) {
-        LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
-                   << vma.end << "]";
-        return false;
-    }
-    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)) {
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index 3968c09..2e89c41 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -56,7 +56,6 @@
 
 cc_binary {
     name: "showmap",
-    host_supported: true,
     cflags: [
         "-Wall",
         "-Werror",
@@ -67,12 +66,6 @@
         "libbase",
         "libmeminfo",
     ],
-
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    },
 }
 
 cc_binary {
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
index b245f2a..47881ed 100644
--- a/libmeminfo/tools/procmem.cpp
+++ b/libmeminfo/tools/procmem.cpp
@@ -17,7 +17,6 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <stdlib.h>
-#include <sys/mman.h>
 #include <unistd.h>
 
 #include <iostream>
@@ -60,25 +59,25 @@
 
 static void print_separator(std::stringstream& ss) {
     if (show_wss) {
-        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n",
+        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  %7s  %s\n",
+    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  %7s  %s\n", "WRss",
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "WRss",
                                             "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi",
-                                            "Flags", "Name");
+                                            "Name");
     } else {
-        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n",
-                                            "Vss", "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl",
-                                            "PrDi", "Flags", "Name");
+        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);
 }
@@ -104,15 +103,7 @@
             continue;
         }
         print_stats(ss, vma_stats);
-
-        // TODO: b/141711064 fix libprocinfo to record (p)rivate or (s)hared flag
-        // for now always report as private
-        std::string flags_str("---p");
-        if (vma.flags & PROT_READ) flags_str[0] = 'r';
-        if (vma.flags & PROT_WRITE) flags_str[1] = 'w';
-        if (vma.flags & PROT_EXEC) flags_str[2] = 'x';
-
-        ss << ::android::base::StringPrintf("%7s  ", flags_str.c_str()) << vma.name << std::endl;
+        ss << vma.name << std::endl;
     }
     print_separator(ss);
     print_stats(ss, proc_stats);
diff --git a/libmeminfo/tools/showmap.cpp b/libmeminfo/tools/showmap.cpp
index 72ab0f3..a80fa76 100644
--- a/libmeminfo/tools/showmap.cpp
+++ b/libmeminfo/tools/showmap.cpp
@@ -18,8 +18,6 @@
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/signal.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -58,7 +56,7 @@
 static VmaInfo g_total;
 static std::vector<VmaInfo> g_vmas;
 
-[[noreturn]] static void usage(const char* progname, int exit_status) {
+[[noreturn]] static void usage(int exit_status) {
     fprintf(stderr,
             "%s [-aqtv] [-f FILE] PID\n"
             "-a\taddresses (show virtual memory map)\n"
@@ -66,7 +64,7 @@
             "-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",
-            progname);
+            getprogname());
 
     exit(exit_status);
 }
@@ -140,9 +138,6 @@
     if (!g_verbose && !g_show_addr) {
         printf("   # ");
     }
-    if (g_verbose) {
-        printf(" flags ");
-    }
     printf(" object\n");
 }
 
@@ -154,9 +149,6 @@
     if (!g_verbose && !g_show_addr) {
         printf("---- ");
     }
-    if (g_verbose) {
-        printf("------ ");
-    }
     printf("------------------------------\n");
 }
 
@@ -176,18 +168,6 @@
     if (!g_verbose && !g_show_addr) {
         printf("%4" PRIu32 " ", v.count);
     }
-    if (g_verbose) {
-        if (total) {
-            printf("       ");
-        } else {
-            std::string flags_str("---");
-            if (v.vma.flags & PROT_READ) flags_str[0] = 'r';
-            if (v.vma.flags & PROT_WRITE) flags_str[1] = 'w';
-            if (v.vma.flags & PROT_EXEC) flags_str[2] = 'x';
-
-            printf("%6s ", flags_str.c_str());
-        }
-    }
 }
 
 static int showmap(void) {
@@ -259,22 +239,22 @@
                 g_filename = optarg;
                 break;
             case 'h':
-                usage(argv[0], EXIT_SUCCESS);
+                usage(EXIT_SUCCESS);
             default:
-                usage(argv[0], EXIT_FAILURE);
+                usage(EXIT_FAILURE);
         }
     }
 
     if (g_filename.empty()) {
         if ((argc - 1) < optind) {
             fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
-            usage(argv[0], EXIT_FAILURE);
+            usage(EXIT_FAILURE);
         }
 
         g_pid = atoi(argv[optind]);
         if (g_pid <= 0) {
             fprintf(stderr, "Invalid process id %s\n", argv[optind]);
-            usage(argv[0], EXIT_FAILURE);
+            usage(EXIT_FAILURE);
         }
 
         g_filename = ::android::base::StringPrintf("/proc/%d/smaps", g_pid);
diff --git a/libmeminfo/vts/Android.bp b/libmeminfo/vts/Android.bp
index a92f669..5a3a23b 100644
--- a/libmeminfo/vts/Android.bp
+++ b/libmeminfo/vts/Android.bp
@@ -12,22 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_defaults {
-    name: "vts_meminfo_defaults",
+cc_test {
+    name: "vts_meminfo_test",
     defaults: ["libmeminfo_defaults"],
     srcs: ["vts_meminfo_test.cpp"],
     static_libs: ["libmeminfo"],
 }
-
-cc_test {
-    name: "vts_meminfo_test",
-    defaults: ["vts_meminfo_defaults"],
-}
-
-cc_test {
-    name: "vts_core_meminfo_test",
-    defaults: ["vts_meminfo_defaults"],
-    test_suites: ["vts-core"],
-    auto_gen_config: true,
-    test_min_api_level: 29,
-}
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index c7dff5a..4e4554a 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -15,6 +15,8 @@
         "liblog",
         "libbase",
         "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
         "libutils",
         "android.hardware.memtrack@1.0",
     ],
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index f20df19..8f01e50 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -22,7 +22,6 @@
 
 cc_library {
     name: "libmemunreachable",
-    vendor_available: true,
     defaults: ["libmemunreachable_defaults"],
     srcs: [
         "Allocator.cpp",
@@ -49,24 +48,11 @@
     },
     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_unit_test",
-    defaults: ["libmemunreachable_defaults"],
     host_supported: true,
     srcs: [
         "tests/Allocator_test.cpp",
@@ -81,9 +67,8 @@
                 "tests/MemUnreachable_test.cpp",
                 "tests/ThreadCapture_test.cpp",
             ],
-            static_libs: [
+            shared_libs: [
                 "libmemunreachable",
-                "libc_malloc_debug_backtrace",
             ],
         },
         host: {
@@ -105,8 +90,6 @@
 cc_test {
     name: "memunreachable_binder_test",
     defaults: ["libmemunreachable_defaults"],
-    require_root: true,
-
     srcs: [
         "tests/Binder_test.cpp",
     ],
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index c4add19..299c320 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -25,7 +25,6 @@
 #include <unordered_map>
 
 #include <android-base/macros.h>
-#include <android-base/strings.h>
 #include <backtrace.h>
 
 #include "Allocator.h"
@@ -251,8 +250,7 @@
     } else if (mapping_name == current_lib) {
       // .rodata or .data section
       globals_mappings.emplace_back(*it);
-    } else if (mapping_name == "[anon:libc_malloc]" ||
-               android::base::StartsWith(mapping_name, "[anon:scudo:")) {
+    } else if (mapping_name == "[anon:libc_malloc]") {
       // named malloc mapping
       heap_mappings.emplace_back(*it);
     } else if (has_prefix(mapping_name, "[anon:dalvik-")) {
@@ -282,12 +280,6 @@
 }
 
 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/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
index dc863c9..655e826 100644
--- a/libmemunreachable/ScopedDisableMalloc.h
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -34,8 +34,8 @@
 
   void Disable() {
     if (!disabled_) {
-      disabled_ = true;
       malloc_disable();
+      disabled_ = true;
     }
   }
 
@@ -71,7 +71,8 @@
 
 class ScopedDisableMallocTimeout {
  public:
-  explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::seconds(10))
+  explicit ScopedDisableMallocTimeout(
+      std::chrono::milliseconds timeout = std::chrono::milliseconds(2000))
       : timeout_(timeout), timed_out_(false), disable_malloc_() {
     Disable();
   }
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index 011443f..c028eab 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -28,45 +28,38 @@
 namespace android {
 
 struct Leak {
-  uintptr_t begin = 0;
-  size_t size = 0;
+  uintptr_t begin;
+  size_t size;
 
-  size_t referenced_count = 0;
-  size_t referenced_size = 0;
+  size_t referenced_count;
+  size_t 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 similar_count;
+  size_t similar_size;
+  size_t similar_referenced_count;
+  size_t similar_referenced_size;
 
-  size_t total_size = 0;
+  size_t total_size;
 
   static const size_t contents_length = 32;
-  char contents[contents_length] = {};
+  char contents[contents_length];
 
   struct Backtrace {
-    size_t num_frames = 0;
+    size_t num_frames;
 
     static const size_t max_frames = 16;
-    uintptr_t frames[max_frames] = {};
-
-    size_t reserved[8] = {};
+    uintptr_t frames[max_frames];
   } backtrace;
 
-  size_t reserved[8] = {};
-
   std::string ToString(bool log_contents) const;
 };
 
 struct UnreachableMemoryInfo {
   std::vector<Leak> leaks;
-  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] = {};
+  size_t num_leaks;
+  size_t leak_bytes;
+  size_t num_allocations;
+  size_t allocation_bytes;
 
   UnreachableMemoryInfo() {}
   ~UnreachableMemoryInfo();
diff --git a/libmemunreachable/libmemunreachable.map b/libmemunreachable/libmemunreachable.map
deleted file mode 100644
index 0d0d954..0000000
--- a/libmemunreachable/libmemunreachable.map
+++ /dev/null
@@ -1,13 +0,0 @@
-LIBMEMUNREACHABLE {
-  global:
-    LogUnreachableMemory;
-    NoLeaks;
-    extern "C++" {
-      android::GetUnreachableMemory*;
-      android::GetUnreachableMemoryString*;
-      android::Leak::*;
-      android::UnreachableMemoryInfo::*;
-    };
-  local:
-    *;
-};
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index 9cb1623..bba0c6d 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -47,8 +47,7 @@
 
 // Trick the compiler into thinking a value on the stack is still referenced.
 static void Ref(void** ptr) {
-  void** volatile storage;
-  storage = ptr;
+  write(0, ptr, 0);
 }
 
 class MemunreachableTest : public ::testing::Test {
@@ -265,12 +264,4 @@
   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/libmodprobe/Android.bp b/libmodprobe/Android.bp
deleted file mode 100644
index 78da46c..0000000
--- a/libmodprobe/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-cc_library_static {
-    name: "libmodprobe",
-    cflags: [
-        "-Werror",
-    ],
-    vendor_available: true,
-    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/OWNERS b/libmodprobe/OWNERS
deleted file mode 100644
index 4b770b1..0000000
--- a/libmodprobe/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-tomcherry@google.com
-smuckle@google.com
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
deleted file mode 100644
index 333fc55..0000000
--- a/libmodprobe/include/modprobe/modprobe.h
+++ /dev/null
@@ -1,65 +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.
- */
-
-#pragma once
-
-#include <set>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-class Modprobe {
-  public:
-    Modprobe(const std::vector<std::string>&);
-
-    bool LoadListedModules(bool strict = true);
-    bool LoadWithAliases(const std::string& module_name, bool strict,
-                         const std::string& parameters = "");
-    bool Remove(const std::string& module_name);
-    std::vector<std::string> ListModules(const std::string& pattern);
-    bool GetAllDependencies(const std::string& module, std::vector<std::string>* pre_dependencies,
-                            std::vector<std::string>* dependencies,
-                            std::vector<std::string>* post_dependencies);
-    void EnableBlacklist(bool enable);
-    void EnableVerbose(bool enable);
-
-  private:
-    std::string MakeCanonical(const std::string& module_path);
-    bool InsmodWithDeps(const std::string& module_name, const std::string& parameters);
-    bool Insmod(const std::string& path_name, const std::string& parameters);
-    bool Rmmod(const std::string& module_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);
-    bool ParseBlacklistCallback(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_;
-    std::set<std::string> module_blacklist_;
-    std::unordered_set<std::string> module_loaded_;
-    bool blacklist_enabled = false;
-};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
deleted file mode 100644
index 6b9107f..0000000
--- a/libmodprobe/libmodprobe.cpp
+++ /dev/null
@@ -1,433 +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 <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;
-}
-
-bool Modprobe::ParseBlacklistCallback(const std::vector<std::string>& args) {
-    auto it = args.begin();
-    const std::string& type = *it++;
-
-    if (type != "blacklist") {
-        LOG(ERROR) << "non-blacklist line encountered in modules.blacklist";
-        return false;
-    }
-
-    if (args.size() != 2) {
-        LOG(ERROR) << "lines in modules.blacklist must have exactly 2 entries, not " << args.size();
-        return false;
-    }
-
-    const std::string& module = *it++;
-
-    const std::string& canonical_name = MakeCanonical(module);
-    if (canonical_name.empty()) {
-        return false;
-    }
-    this->module_blacklist_.emplace(canonical_name);
-
-    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);
-
-        auto blacklist_callback = std::bind(&Modprobe::ParseBlacklistCallback, this, _1);
-        ParseCfg(base_path + "/modules.blacklist", blacklist_callback);
-    }
-
-    android::base::SetMinimumLogSeverity(android::base::INFO);
-}
-
-void Modprobe::EnableBlacklist(bool enable) {
-    blacklist_enabled = enable;
-}
-
-void Modprobe::EnableVerbose(bool enable) {
-    if (enable) {
-        android::base::SetMinimumLogSeverity(android::base::VERBOSE);
-    } else {
-        android::base::SetMinimumLogSeverity(android::base::INFO);
-    }
-}
-
-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, const std::string& parameters) {
-    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) {
-        LOG(VERBOSE) << "Loading hard dep for '" << module_name << "': " << *dep;
-        if (!LoadWithAliases(*dep, true)) {
-            return false;
-        }
-    }
-
-    // try to load soft pre-dependencies
-    for (const auto& [module, softdep] : module_pre_softdep_) {
-        if (module_name == module) {
-            LOG(VERBOSE) << "Loading soft pre-dep for '" << module << "': " << softdep;
-            LoadWithAliases(softdep, false);
-        }
-    }
-
-    // load target module itself with args
-    if (!Insmod(dependencies[0], parameters)) {
-        return false;
-    }
-
-    // try to load soft post-dependencies
-    for (const auto& [module, softdep] : module_post_softdep_) {
-        if (module_name == module) {
-            LOG(VERBOSE) << "Loading soft post-dep for '" << module << "': " << softdep;
-            LoadWithAliases(softdep, false);
-        }
-    }
-
-    return true;
-}
-
-bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict,
-                               const std::string& parameters) {
-    auto canonical_name = MakeCanonical(module_name);
-    if (module_loaded_.count(canonical_name)) {
-        return true;
-    }
-
-    std::set<std::string> modules_to_load = {canonical_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;
-        LOG(VERBOSE) << "Found alias for '" << module_name << "': '" << aliased_module;
-        if (module_loaded_.count(MakeCanonical(aliased_module))) 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, parameters)) module_loaded = true;
-    }
-
-    if (strict && !module_loaded) {
-        LOG(ERROR) << "LoadWithAliases was unable to load " << module_name;
-        return false;
-    }
-    return true;
-}
-
-bool Modprobe::LoadListedModules(bool strict) {
-    auto ret = true;
-    for (const auto& module : module_load_) {
-        if (!LoadWithAliases(module, true)) {
-            ret = false;
-            if (strict) break;
-        }
-    }
-    return ret;
-}
-
-bool Modprobe::Remove(const std::string& module_name) {
-    auto dependencies = GetDependencies(MakeCanonical(module_name));
-    if (dependencies.empty()) {
-        LOG(ERROR) << "Empty dependencies for module " << module_name;
-        return false;
-    }
-    if (!Rmmod(dependencies[0])) {
-        return false;
-    }
-    for (auto dep = dependencies.begin() + 1; dep != dependencies.end(); ++dep) {
-        Rmmod(*dep);
-    }
-    return true;
-}
-
-std::vector<std::string> Modprobe::ListModules(const std::string& pattern) {
-    std::vector<std::string> rv;
-    for (const auto& [module, deps] : module_deps_) {
-        // Attempt to match both the canonical module name and the module filename.
-        if (!fnmatch(pattern.c_str(), module.c_str(), 0)) {
-            rv.emplace_back(module);
-        } else if (!fnmatch(pattern.c_str(), basename(deps[0].c_str()), 0)) {
-            rv.emplace_back(deps[0]);
-        }
-    }
-    return rv;
-}
-
-bool Modprobe::GetAllDependencies(const std::string& module,
-                                  std::vector<std::string>* pre_dependencies,
-                                  std::vector<std::string>* dependencies,
-                                  std::vector<std::string>* post_dependencies) {
-    std::string canonical_name = MakeCanonical(module);
-    if (pre_dependencies) {
-        pre_dependencies->clear();
-        for (const auto& [it_module, it_softdep] : module_pre_softdep_) {
-            if (canonical_name == it_module) {
-                pre_dependencies->emplace_back(it_softdep);
-            }
-        }
-    }
-    if (dependencies) {
-        dependencies->clear();
-        auto hard_deps = GetDependencies(canonical_name);
-        if (hard_deps.empty()) {
-            return false;
-        }
-        for (auto dep = hard_deps.rbegin(); dep != hard_deps.rend(); dep++) {
-            dependencies->emplace_back(*dep);
-        }
-    }
-    if (post_dependencies) {
-        for (const auto& [it_module, it_softdep] : module_post_softdep_) {
-            if (canonical_name == it_module) {
-                post_dependencies->emplace_back(it_softdep);
-            }
-        }
-    }
-    return true;
-}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
deleted file mode 100644
index 8bebe4c..0000000
--- a/libmodprobe/libmodprobe_ext.cpp
+++ /dev/null
@@ -1,91 +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 <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, const std::string& parameters) {
-    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;
-    }
-
-    auto canonical_name = MakeCanonical(path_name);
-    std::string options = "";
-    auto options_iter = module_options_.find(canonical_name);
-    if (options_iter != module_options_.end()) {
-        options = options_iter->second;
-    }
-    if (!parameters.empty()) {
-        options = options + " " + parameters;
-    }
-
-    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
-            module_loaded_.emplace(canonical_name);
-            return true;
-        }
-        LOG(ERROR) << "Failed to insmod '" << path_name << "' with args '" << options << "'";
-        return false;
-    }
-
-    LOG(INFO) << "Loaded kernel module " << path_name;
-    module_loaded_.emplace(canonical_name);
-    return true;
-}
-
-bool Modprobe::Rmmod(const std::string& module_name) {
-    auto canonical_name = MakeCanonical(module_name);
-    int ret = syscall(__NR_delete_module, canonical_name.c_str(), O_NONBLOCK);
-    if (ret != 0) {
-        PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
-        return false;
-    }
-    module_loaded_.erase(canonical_name);
-    return true;
-}
-
-bool Modprobe::ModuleExists(const std::string& module_name) {
-    struct stat fileStat;
-    if (blacklist_enabled && module_blacklist_.count(module_name)) {
-        LOG(INFO) << "module " << module_name << " is blacklisted";
-        return false;
-    }
-    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)) {
-        LOG(INFO) << "module " << module_name << " does not exist";
-        return false;
-    }
-    if (!S_ISREG(fileStat.st_mode)) {
-        LOG(INFO) << "module " << module_name << " is not a regular file";
-        return false;
-    }
-    return true;
-}
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
deleted file mode 100644
index 7d817b1..0000000
--- a/libmodprobe/libmodprobe_ext_test.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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, const std::string& parameters) {
-    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;
-    }
-    if (!parameters.empty()) {
-        options = options + " " + parameters;
-    }
-
-    modules_loaded.emplace_back(path_name + options);
-    return true;
-}
-
-bool Modprobe::Rmmod(const std::string& module_name) {
-    for (auto it = modules_loaded.begin(); it != modules_loaded.end(); it++) {
-        if (*it == module_name) {
-            modules_loaded.erase(it);
-            return true;
-        }
-    }
-    return false;
-}
-
-bool Modprobe::ModuleExists(const std::string& module_name) {
-    auto deps = GetDependencies(module_name);
-    if (blacklist_enabled && module_blacklist_.count(module_name)) {
-        return false;
-    }
-    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
deleted file mode 100644
index a711631..0000000
--- a/libmodprobe/libmodprobe_test.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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",
-    };
-
-    std::vector<std::string> expected_after_remove = {
-            "/test14.ko", "/test15.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_blacklist =
-            "blacklist test9.ko\n"
-            "blacklist test3.ko\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;
-    auto dir_path = std::string(dir.path);
-    ASSERT_TRUE(android::base::WriteStringToFile(modules_alias, dir_path + "/modules.alias", 0600,
-                                                 getuid(), getgid()));
-
-    ASSERT_TRUE(android::base::WriteStringToFile(modules_dep, dir_path + "/modules.dep", 0600,
-                                                 getuid(), getgid()));
-    ASSERT_TRUE(android::base::WriteStringToFile(modules_softdep, dir_path + "/modules.softdep",
-                                                 0600, getuid(), getgid()));
-    ASSERT_TRUE(android::base::WriteStringToFile(modules_options, dir_path + "/modules.options",
-                                                 0600, getuid(), getgid()));
-    ASSERT_TRUE(android::base::WriteStringToFile(modules_load, dir_path + "/modules.load", 0600,
-                                                 getuid(), getgid()));
-    ASSERT_TRUE(android::base::WriteStringToFile(modules_blacklist, dir_path + "/modules.blacklist",
-                                                 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);
-
-    EXPECT_TRUE(m.Remove("test4"));
-
-    GTEST_LOG_(INFO) << "Expected modules loaded after removing test4 (in order):";
-    for (auto i = expected_after_remove.begin(); i != expected_after_remove.end(); ++i) {
-        *i = dir.path + *i;
-        GTEST_LOG_(INFO) << "\"" << *i << "\"";
-    }
-    GTEST_LOG_(INFO) << "Actual modules loaded after removing test4 (in order):";
-    for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
-        GTEST_LOG_(INFO) << "\"" << *i << "\"";
-    }
-
-    EXPECT_TRUE(modules_loaded == expected_after_remove);
-
-    m.EnableBlacklist(true);
-    EXPECT_FALSE(m.LoadWithAliases("test4", true));
-}
diff --git a/libpackagelistparser/.clang-format b/libnativebridge/.clang-format
similarity index 100%
copy from libpackagelistparser/.clang-format
copy to libnativebridge/.clang-format
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
new file mode 100644
index 0000000..10d42e4
--- /dev/null
+++ b/libnativebridge/Android.bp
@@ -0,0 +1,61 @@
+cc_defaults {
+    name: "libnativebridge-defaults",
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    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/OWNERS b/libnativebridge/OWNERS
new file mode 100644
index 0000000..daf87f4
--- /dev/null
+++ b/libnativebridge/OWNERS
@@ -0,0 +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
new file mode 100644
index 0000000..e9c9500
--- /dev/null
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -0,0 +1,418 @@
+/*
+ * 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 NATIVE_BRIDGE_H_
+#define NATIVE_BRIDGE_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;
+
+// Function pointer type for sigaction. This is mostly the signature of a signal handler, except
+// for the return type. The runtime needs to know whether the signal was handled or should be given
+// 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 struct NativeBridgeRuntimeCallbacks* runtime_callbacks);
+
+// Quick check whether a native bridge will be needed. This is based off of the instruction set
+// of the process.
+bool NeedsNativeBridge(const char* instruction_set);
+
+// Do the early initialization part of the native bridge, if necessary. This should be done under
+// high privileges.
+bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set);
+
+// Initialize the native bridge, if any. Should be called by Runtime::DidForkFromZygote. The JNIEnv*
+// will be used to modify the app environment for the bridge.
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set);
+
+// Unload the native bridge, if any. Should be called by Runtime::DidForkFromZygote.
+void UnloadNativeBridge();
+
+// Check whether a native bridge is available (opened or initialized). Requires a prior call to
+// LoadNativeBridge.
+bool NativeBridgeAvailable();
+
+// Check whether a native bridge is available (initialized). Requires a prior call to
+// LoadNativeBridge & InitializeNativeBridge.
+bool NativeBridgeInitialized();
+
+// Load a shared library that is supported by the native bridge.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeLoadLibraryExt() instead in namespace scenario.
+void* NativeBridgeLoadLibrary(const char* libpath, int flag);
+
+// Get a native bridge trampoline for specified native method.
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len);
+
+// True if native library paths are valid and is for an ABI that is supported by native bridge.
+// The *libpath* must point to a library.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeIsPathSupported() instead in namespace scenario.
+bool NativeBridgeIsSupported(const char* libpath);
+
+// Returns the version number of the native bridge. This information is available after a
+// successful LoadNativeBridge() and before closing it, that is, as long as NativeBridgeAvailable()
+// returns true. Returns 0 otherwise.
+uint32_t NativeBridgeGetVersion();
+
+// Returns a signal handler that the bridge would like to be managed. Only valid for a native
+// bridge supporting the version 2 interface. Will return null if the bridge does not support
+// version 2, or if it doesn't have a signal handler it wants to be known.
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal);
+
+// Returns whether we have seen a native bridge error. This could happen because the library
+// was not found, rejected, could not be initialized and so on.
+//
+// This functionality is mainly for testing.
+bool NativeBridgeError();
+
+// Returns whether a given string is acceptable as a native bridge library filename.
+//
+// This functionality is exposed mainly for testing.
+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. 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.
+// This is reflection of dlerror() for native bridge.
+const char* NativeBridgeGetError();
+
+struct native_bridge_namespace_t;
+
+// True if native library paths are valid and is for an ABI that is supported by native bridge.
+// Different from NativeBridgeIsSupported(), the *path* here must be a directory containing
+// libraries of an ABI.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeIsSupported() instead in non-namespace scenario.
+bool NativeBridgeIsPathSupported(const char* path);
+
+// Initializes anonymous namespace.
+// NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker.
+//
+// The anonymous namespace is used in the case when a NativeBridge implementation
+// cannot identify the caller of dlopen/dlsym which happens for the code not loaded
+// by dynamic linker; for example calls from the mono-compiled code.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+                                        const char* anon_ns_library_path);
+
+// Create new namespace in which native libraries will be loaded.
+// NativeBridge's peer of android_create_namespace() of dynamic linker.
+//
+// The libraries in the namespace are searched by folowing order:
+// 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
+// 2. In directories specified by DT_RUNPATH of the "needed by" binary.
+// 3. deault_library_path (This of this as namespace-local default library path)
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Should not use in non-namespace scenario.
+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(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.
+// NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace
+// extension.
+//
+// Starting with v3, NativeBridge has two scenarios: with/without namespace.
+// Use NativeBridgeLoadLibrary() instead in non-namespace scenario.
+void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
+                                 struct native_bridge_namespace_t* ns);
+
+// 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 {
+  // Version number of the interface.
+  uint32_t version;
+
+  // Initialize native bridge. Native bridge's internal implementation must ensure MT safety and
+  // that the native bridge is initialized only once. Thus it is OK to call this interface for an
+  // already initialized native bridge.
+  //
+  // Parameters:
+  //   runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks.
+  // Returns:
+  //   true if initialization was successful.
+  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.
+  //
+  // Parameters:
+  //   libpath [IN] path to the shared library
+  //   flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
+  // Returns:
+  //   The opaque handle of the shared library if sucessful, otherwise NULL
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Use loadLibraryExt instead in namespace scenario.
+  void* (*loadLibrary)(const char* libpath, int flag);
+
+  // Get a native bridge trampoline for specified native method. The trampoline has same
+  // sigature as the native method.
+  //
+  // Parameters:
+  //   handle [IN] the handle returned from loadLibrary
+  //   shorty [IN] short descriptor of native method
+  //   len [IN] length of shorty
+  // Returns:
+  //   address of trampoline if successful, otherwise NULL
+  void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len);
+
+  // Check whether native library is valid and is for an ABI that is supported by native bridge.
+  //
+  // Parameters:
+  //   libpath [IN] path to the shared library
+  // Returns:
+  //   TRUE if library is supported by native bridge, FALSE otherwise
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Use isPathSupported instead in namespace scenario.
+  bool (*isSupported)(const char* libpath);
+
+  // Provide environment values required by the app running with native bridge according to the
+  // instruction set.
+  //
+  // Parameters:
+  //   instruction_set [IN] the instruction set of the app
+  // Returns:
+  //   NULL if not supported by native bridge.
+  //   Otherwise, return all environment values to be set after fork.
+  const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set);
+
+  // Added callbacks in version 2.
+
+  // Check whether the bridge is compatible with the given version. A bridge may decide not to be
+  // forwards- or backwards-compatible, and libnativebridge will then stop using it.
+  //
+  // Parameters:
+  //   bridge_version [IN] the version of libnativebridge.
+  // Returns:
+  //   true if the native bridge supports the given version of libnativebridge.
+  bool (*isCompatibleWith)(uint32_t bridge_version);
+
+  // A callback to retrieve a native bridge's signal handler for the specified signal. The runtime
+  // will ensure that the signal handler is being called after the runtime's own handler, but before
+  // all chained handlers. The native bridge should not try to install the handler by itself, as
+  // that will potentially lead to cycles.
+  //
+  // Parameters:
+  //   signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is
+  //                 supported by the runtime.
+  // Returns:
+  //   NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the
+  //   runtime.
+  //   Otherwise, a pointer to the signal handler.
+  NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal);
+
+  // Added callbacks in version 3.
+
+  // Decrements the reference count on the dynamic library handler. If the reference count drops
+  // to zero then the dynamic library is unloaded.
+  //
+  // Parameters:
+  //   handle [IN] the handler of a dynamic library.
+  //
+  // Returns:
+  //   0 on success, and nonzero on error.
+  int (*unloadLibrary)(void* handle);
+
+  // Dump the last failure message of native bridge when fail to load library or search symbol.
+  //
+  // Parameters:
+  //
+  // Returns:
+  //   A string describing the most recent error that occurred when load library
+  //   or lookup symbol via native bridge.
+  const char* (*getError)();
+
+  // Check whether library paths are supported by native bridge.
+  //
+  // Parameters:
+  //   library_path [IN] search paths for native libraries (directories separated by ':')
+  // Returns:
+  //   TRUE if libraries within search paths are supported by native bridge, FALSE otherwise
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Use isSupported instead in non-namespace scenario.
+  bool (*isPathSupported)(const char* library_path);
+
+  // Initializes anonymous namespace at native bridge side.
+  // NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker.
+  //
+  // The anonymous namespace is used in the case when a NativeBridge implementation
+  // cannot identify the caller of dlopen/dlsym which happens for the code not loaded
+  // by dynamic linker; for example calls from the mono-compiled code.
+  //
+  // Parameters:
+  //   public_ns_sonames [IN] the name of "public" libraries.
+  //   anon_ns_library_path [IN] the library search path of (anonymous) namespace.
+  // Returns:
+  //   true if the pass is ok.
+  //   Otherwise, false.
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Should not use in non-namespace scenario.
+  bool (*initAnonymousNamespace)(const char* public_ns_sonames, const char* anon_ns_library_path);
+
+  // Create new namespace in which native libraries will be loaded.
+  // NativeBridge's peer of android_create_namespace() of dynamic linker.
+  //
+  // Parameters:
+  //   name [IN] the name of the namespace.
+  //   ld_library_path [IN] the first set of library search paths of the namespace.
+  //   default_library_path [IN] the second set of library search path of the namespace.
+  //   type [IN] the attribute of the namespace.
+  //   permitted_when_isolated_path [IN] the permitted path for isolated namespace(if it is).
+  //   parent_ns [IN] the pointer of the parent namespace to be inherited from.
+  // Returns:
+  //   native_bridge_namespace_t* for created namespace or nullptr in the case of error.
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Should not use in non-namespace scenario.
+  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.
+  //
+  // Parameters:
+  //   from [IN] the namespace where libraries are accessed.
+  //   to [IN] the namespace where libraries are loaded.
+  //   shared_libs_sonames [IN] the libraries to be shared.
+  //
+  // Returns:
+  //   Whether successed or not.
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Should not use in non-namespace scenario.
+  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
+  // extension.
+  //
+  // Parameters:
+  //   libpath [IN] path to the shared library
+  //   flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h
+  //   ns [IN] the pointer of the namespace in which the library should be loaded.
+  // Returns:
+  //   The opaque handle of the shared library if sucessful, otherwise NULL
+  //
+  // Starting with v3, NativeBridge has two scenarios: with/without namespace.
+  // Use loadLibrary instead in non-namespace scenario.
+  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.
+  // With O release this namespace can be different from the default namespace.
+  // For the devices without enable vendor namespaces this function should return null
+  //
+  // Returns:
+  //   vendor namespace or null if it was not set up for the device
+  //
+  // 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.
+struct NativeBridgeRuntimeCallbacks {
+  // Get shorty of a Java method. The shorty is supposed to be persistent in memory.
+  //
+  // Parameters:
+  //   env [IN] pointer to JNIenv.
+  //   mid [IN] Java methodID.
+  // Returns:
+  //   short descriptor for method.
+  const char* (*getMethodShorty)(JNIEnv* env, jmethodID mid);
+
+  // Get number of native methods for specified class.
+  //
+  // Parameters:
+  //   env [IN] pointer to JNIenv.
+  //   clazz [IN] Java class object.
+  // Returns:
+  //   number of native methods.
+  uint32_t (*getNativeMethodCount)(JNIEnv* env, jclass clazz);
+
+  // Get at most 'method_count' native methods for specified class 'clazz'. Results are outputed
+  // via 'methods' [OUT]. The signature pointer in JNINativeMethod is reused as the method shorty.
+  //
+  // Parameters:
+  //   env [IN] pointer to JNIenv.
+  //   clazz [IN] Java class object.
+  //   methods [OUT] array of method with the name, shorty, and fnPtr.
+  //   method_count [IN] max number of elements in methods.
+  // Returns:
+  //   number of method it actually wrote to methods.
+  uint32_t (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
+                               uint32_t method_count);
+};
+
+#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
new file mode 100644
index 0000000..9adba9a
--- /dev/null
+++ b/libnativebridge/native_bridge.cc
@@ -0,0 +1,646 @@
+/*
+ * 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 LOG_TAG "nativebridge"
+
+#include "nativebridge/native_bridge.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <cstring>
+
+#include <android-base/macros.h>
+#include <log/log.h>
+
+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;
+    const char* cpu_abi;
+    const char* cpu_abi2;
+    const char* *supported_abis;
+    int32_t abi_count;
+};
+
+// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks.
+static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf";
+
+enum class NativeBridgeState {
+  kNotSetup,                        // Initial state.
+  kOpened,                          // After successful dlopen.
+  kPreInitialized,                  // After successful pre-initialization.
+  kInitialized,                     // After successful initialization.
+  kClosed                           // Closed or errors.
+};
+
+static constexpr const char* kNotSetupString = "kNotSetup";
+static constexpr const char* kOpenedString = "kOpened";
+static constexpr const char* kPreInitializedString = "kPreInitialized";
+static constexpr const char* kInitializedString = "kInitialized";
+static constexpr const char* kClosedString = "kClosed";
+
+static const char* GetNativeBridgeStateString(NativeBridgeState state) {
+  switch (state) {
+    case NativeBridgeState::kNotSetup:
+      return kNotSetupString;
+
+    case NativeBridgeState::kOpened:
+      return kOpenedString;
+
+    case NativeBridgeState::kPreInitialized:
+      return kPreInitializedString;
+
+    case NativeBridgeState::kInitialized:
+      return kInitializedString;
+
+    case NativeBridgeState::kClosed:
+      return kClosedString;
+  }
+}
+
+// Current state of the native bridge.
+static NativeBridgeState state = NativeBridgeState::kNotSetup;
+
+// The version of NativeBridge implementation.
+// Different Nativebridge interface needs the service of different version of
+// Nativebridge implementation.
+// Used by isCompatibleWith() which is introduced in v2.
+enum NativeBridgeImplementationVersion {
+  // first version, not used.
+  DEFAULT_VERSION = 1,
+  // The version which signal semantic is introduced.
+  SIGNAL_VERSION = 2,
+  // The version which namespace semantic is introduced.
+  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.
+static bool had_error = false;
+
+// Handle of the loaded library.
+static void* native_bridge_handle = nullptr;
+// Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized
+// later.
+static const NativeBridgeCallbacks* callbacks = nullptr;
+// Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge.
+static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr;
+
+// The app's code cache directory.
+static char* app_code_cache_dir = nullptr;
+
+// Code cache directory (relative to the application private directory)
+// Ideally we'd like to call into framework to retrieve this name. However that's considered an
+// implementation detail and will require either hacks or consistent refactorings. We compromise
+// and hard code the directory name again here.
+static constexpr const char* kCodeCacheDir = "code_cache";
+
+// Characters allowed in a native bridge filename. The first character must
+// be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-].
+static bool CharacterAllowed(char c, bool first) {
+  if (first) {
+    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
+  } else {
+    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') ||
+           (c == '.') || (c == '_') || (c == '-');
+  }
+}
+
+static void ReleaseAppCodeCacheDir() {
+  if (app_code_cache_dir != nullptr) {
+    delete[] app_code_cache_dir;
+    app_code_cache_dir = nullptr;
+  }
+}
+
+// We only allow simple names for the library. It is supposed to be a file in
+// /system/lib or /vendor/lib. Only allow a small range of characters, that is
+// names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
+bool NativeBridgeNameAcceptable(const char* nb_library_filename) {
+  const char* ptr = nb_library_filename;
+  if (*ptr == 0) {
+    // Emptry string. Allowed, means no native bridge.
+    return true;
+  } else {
+    // First character must be [a-zA-Z].
+    if (!CharacterAllowed(*ptr, true))  {
+      // Found an invalid fist character, don't accept.
+      ALOGE("Native bridge library %s has been rejected for first character %c",
+            nb_library_filename,
+            *ptr);
+      return false;
+    } else {
+      // For the rest, be more liberal.
+      ptr++;
+      while (*ptr != 0) {
+        if (!CharacterAllowed(*ptr, false)) {
+          // Found an invalid character, don't accept.
+          ALOGE("Native bridge library %s has been rejected for %c", nb_library_filename, *ptr);
+          return false;
+        }
+        ptr++;
+      }
+    }
+    return true;
+  }
+}
+
+// The policy of invoking Nativebridge changed in v3 with/without namespace.
+// Suggest Nativebridge implementation not maintain backward-compatible.
+static bool isCompatibleWith(const uint32_t version) {
+  // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported
+  // version.
+  if (callbacks == nullptr || callbacks->version == 0 || version == 0) {
+    return false;
+  }
+
+  // If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check.
+  if (callbacks->version >= SIGNAL_VERSION) {
+    return callbacks->isCompatibleWith(version);
+  }
+
+  return true;
+}
+
+static void CloseNativeBridge(bool with_error) {
+  state = NativeBridgeState::kClosed;
+  had_error |= with_error;
+  ReleaseAppCodeCacheDir();
+}
+
+bool LoadNativeBridge(const char* nb_library_filename,
+                      const NativeBridgeRuntimeCallbacks* runtime_cbs) {
+  // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not
+  // multi-threaded, so we do not need locking here.
+
+  if (state != NativeBridgeState::kNotSetup) {
+    // Setup has been called before. Ignore this call.
+    if (nb_library_filename != nullptr) {  // Avoids some log-spam for dalvikvm.
+      ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.",
+            GetNativeBridgeStateString(state));
+    }
+    // Note: counts as an error, even though the bridge may be functional.
+    had_error = true;
+    return false;
+  }
+
+  if (nb_library_filename == nullptr || *nb_library_filename == 0) {
+    CloseNativeBridge(false);
+    return false;
+  } else {
+    if (!NativeBridgeNameAcceptable(nb_library_filename)) {
+      CloseNativeBridge(true);
+    } else {
+      // Try to open the library.
+      void* handle = dlopen(nb_library_filename, RTLD_LAZY);
+      if (handle != nullptr) {
+        callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle,
+                                                                   kNativeBridgeInterfaceSymbol));
+        if (callbacks != nullptr) {
+          if (isCompatibleWith(NAMESPACE_VERSION)) {
+            // Store the handle for later.
+            native_bridge_handle = handle;
+          } else {
+            callbacks = nullptr;
+            dlclose(handle);
+            ALOGW("Unsupported native bridge interface.");
+          }
+        } else {
+          dlclose(handle);
+        }
+      }
+
+      // Two failure conditions: could not find library (dlopen failed), or could not find native
+      // bridge interface (dlsym failed). Both are an error and close the native bridge.
+      if (callbacks == nullptr) {
+        CloseNativeBridge(true);
+      } else {
+        runtime_callbacks = runtime_cbs;
+        state = NativeBridgeState::kOpened;
+      }
+    }
+    return state == NativeBridgeState::kOpened;
+  }
+}
+
+bool NeedsNativeBridge(const char* instruction_set) {
+  if (instruction_set == nullptr) {
+    ALOGE("Null instruction set in NeedsNativeBridge.");
+    return false;
+  }
+  return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0;
+}
+
+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.");
+    CloseNativeBridge(true);
+    return false;
+  }
+
+  if (app_data_dir_in == nullptr) {
+    ALOGE("Application private directory cannot be null.");
+    CloseNativeBridge(true);
+    return false;
+  }
+
+  // Create the path to the application code cache directory.
+  // The memory will be release after Initialization or when the native bridge is closed.
+  const size_t len = strlen(app_data_dir_in) + strlen(kCodeCacheDir) + 2; // '\0' + '/'
+  app_code_cache_dir = new char[len];
+  snprintf(app_code_cache_dir, len, "%s/%s", app_data_dir_in, kCodeCacheDir);
+
+  // Bind-mount /system/lib{,64}/<isa>/cpuinfo to /proc/cpuinfo.
+  // Failure is not fatal and will keep the native bridge in kPreInitialized.
+  state = NativeBridgeState::kPreInitialized;
+
+#ifndef __APPLE__
+  if (instruction_set == nullptr) {
+    return true;
+  }
+  size_t isa_len = strlen(instruction_set);
+  if (isa_len > 10) {
+    // 10 is a loose upper bound on the currently known instruction sets (a tight bound is 7 for
+    // x86_64 [including the trailing \0]). This is so we don't have to change here if there will
+    // be another instruction set in the future.
+    ALOGW("Instruction set %s is malformed, must be less than or equal to 10 characters.",
+          instruction_set);
+    return true;
+  }
+
+  // If the file does not exist, the mount command will fail,
+  // so we save the extra file existence check.
+  char cpuinfo_path[1024];
+
+#if defined(__ANDROID__)
+  snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib"
+#ifdef __LP64__
+      "64"
+#endif  // __LP64__
+      "/%s/cpuinfo", instruction_set);
+#else   // !__ANDROID__
+  // To be able to test on the host, we hardwire a relative path.
+  snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo");
+#endif
+
+  // Bind-mount.
+  if (TEMP_FAILURE_RETRY(mount(cpuinfo_path,        // Source.
+                               "/proc/cpuinfo",     // Target.
+                               nullptr,             // FS type.
+                               MS_BIND,             // Mount flags: bind mount.
+                               nullptr)) == -1) {   // "Data."
+    ALOGW("Failed to bind-mount %s as /proc/cpuinfo: %s", cpuinfo_path, strerror(errno));
+  }
+#else  // __APPLE__
+  UNUSED(instruction_set);
+  ALOGW("Mac OS does not support bind-mounting. Host simulation of native bridge impossible.");
+#endif
+
+  return true;
+}
+
+static void SetCpuAbi(JNIEnv* env, jclass build_class, const char* field, const char* value) {
+  if (value != nullptr) {
+    jfieldID field_id = env->GetStaticFieldID(build_class, field, "Ljava/lang/String;");
+    if (field_id == nullptr) {
+      env->ExceptionClear();
+      ALOGW("Could not find %s field.", field);
+      return;
+    }
+
+    jstring str = env->NewStringUTF(value);
+    if (str == nullptr) {
+      env->ExceptionClear();
+      ALOGW("Could not create string %s.", value);
+      return;
+    }
+
+    env->SetStaticObjectField(build_class, field_id, str);
+  }
+}
+
+// Set up the environment for the bridged app.
+static void SetupEnvironment(const NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) {
+  // Need a JNIEnv* to do anything.
+  if (env == nullptr) {
+    ALOGW("No JNIEnv* to set up app environment.");
+    return;
+  }
+
+  // Query the bridge for environment values.
+  const struct NativeBridgeRuntimeValues* env_values = callbacks->getAppEnv(isa);
+  if (env_values == nullptr) {
+    return;
+  }
+
+  // Keep the JNIEnv clean.
+  jint success = env->PushLocalFrame(16);  // That should be small and large enough.
+  if (success < 0) {
+    // Out of memory, really borked.
+    ALOGW("Out of memory while setting up app environment.");
+    env->ExceptionClear();
+    return;
+  }
+
+  // Reset CPU_ABI & CPU_ABI2 to values required by the apps running with native bridge.
+  if (env_values->cpu_abi != nullptr || env_values->cpu_abi2 != nullptr ||
+      env_values->abi_count >= 0) {
+    jclass bclass_id = env->FindClass("android/os/Build");
+    if (bclass_id != nullptr) {
+      SetCpuAbi(env, bclass_id, "CPU_ABI", env_values->cpu_abi);
+      SetCpuAbi(env, bclass_id, "CPU_ABI2", env_values->cpu_abi2);
+    } else {
+      // For example in a host test environment.
+      env->ExceptionClear();
+      ALOGW("Could not find Build class.");
+    }
+  }
+
+  if (env_values->os_arch != nullptr) {
+    jclass sclass_id = env->FindClass("java/lang/System");
+    if (sclass_id != nullptr) {
+      jmethodID set_prop_id = env->GetStaticMethodID(sclass_id, "setUnchangeableSystemProperty",
+          "(Ljava/lang/String;Ljava/lang/String;)V");
+      if (set_prop_id != nullptr) {
+        // Init os.arch to the value reqired by the apps running with native bridge.
+        env->CallStaticVoidMethod(sclass_id, set_prop_id, env->NewStringUTF("os.arch"),
+            env->NewStringUTF(env_values->os_arch));
+      } else {
+        env->ExceptionClear();
+        ALOGW("Could not find System#setUnchangeableSystemProperty.");
+      }
+    } else {
+      env->ExceptionClear();
+      ALOGW("Could not find System class.");
+    }
+  }
+
+  // Make it pristine again.
+  env->PopLocalFrame(nullptr);
+}
+
+bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
+  // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that
+  // point we are not multi-threaded, so we do not need locking here.
+
+  if (state == NativeBridgeState::kPreInitialized) {
+    // Check for code cache: if it doesn't exist try to create it.
+    struct stat st;
+    if (stat(app_code_cache_dir, &st) == -1) {
+      if (errno == ENOENT) {
+        if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
+          ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+          ReleaseAppCodeCacheDir();
+        }
+      } else {
+        ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+        ReleaseAppCodeCacheDir();
+      }
+    } else if (!S_ISDIR(st.st_mode)) {
+      ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
+      ReleaseAppCodeCacheDir();
+    }
+
+    // If we're still PreInitialized (dind't fail the code cache checks) try to initialize.
+    if (state == NativeBridgeState::kPreInitialized) {
+      if (callbacks->initialize(runtime_callbacks, app_code_cache_dir, instruction_set)) {
+        SetupEnvironment(callbacks, env, instruction_set);
+        state = NativeBridgeState::kInitialized;
+        // We no longer need the code cache path, release the memory.
+        ReleaseAppCodeCacheDir();
+      } else {
+        // Unload the library.
+        dlclose(native_bridge_handle);
+        CloseNativeBridge(true);
+      }
+    }
+  } else {
+    CloseNativeBridge(true);
+  }
+
+  return state == NativeBridgeState::kInitialized;
+}
+
+void UnloadNativeBridge() {
+  // We expect only one place that calls UnloadNativeBridge: Runtime::DidForkFromZygote. At that
+  // point we are not multi-threaded, so we do not need locking here.
+
+  switch(state) {
+    case NativeBridgeState::kOpened:
+    case NativeBridgeState::kPreInitialized:
+    case NativeBridgeState::kInitialized:
+      // Unload.
+      dlclose(native_bridge_handle);
+      CloseNativeBridge(false);
+      break;
+
+    case NativeBridgeState::kNotSetup:
+      // Not even set up. Error.
+      CloseNativeBridge(true);
+      break;
+
+    case NativeBridgeState::kClosed:
+      // Ignore.
+      break;
+  }
+}
+
+bool NativeBridgeError() {
+  return had_error;
+}
+
+bool NativeBridgeAvailable() {
+  return state == NativeBridgeState::kOpened
+      || state == NativeBridgeState::kPreInitialized
+      || state == NativeBridgeState::kInitialized;
+}
+
+bool NativeBridgeInitialized() {
+  // Calls of this are supposed to happen in a state where the native bridge is stable, i.e., after
+  // Runtime::DidForkFromZygote. In that case we do not need a lock.
+  return state == NativeBridgeState::kInitialized;
+}
+
+void* NativeBridgeLoadLibrary(const char* libpath, int flag) {
+  if (NativeBridgeInitialized()) {
+    return callbacks->loadLibrary(libpath, flag);
+  }
+  return nullptr;
+}
+
+void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty,
+                                uint32_t len) {
+  if (NativeBridgeInitialized()) {
+    return callbacks->getTrampoline(handle, name, shorty, len);
+  }
+  return nullptr;
+}
+
+bool NativeBridgeIsSupported(const char* libpath) {
+  if (NativeBridgeInitialized()) {
+    return callbacks->isSupported(libpath);
+  }
+  return false;
+}
+
+uint32_t NativeBridgeGetVersion() {
+  if (NativeBridgeAvailable()) {
+    return callbacks->version;
+  }
+  return 0;
+}
+
+NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(SIGNAL_VERSION)) {
+      return callbacks->getSignalHandler(signal);
+    } else {
+      ALOGE("not compatible with version %d, cannot get signal handler", SIGNAL_VERSION);
+    }
+  }
+  return nullptr;
+}
+
+int NativeBridgeUnloadLibrary(void* handle) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->unloadLibrary(handle);
+    } else {
+      ALOGE("not compatible with version %d, cannot unload library", NAMESPACE_VERSION);
+    }
+  }
+  return -1;
+}
+
+const char* NativeBridgeGetError() {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->getError();
+    } else {
+      return "native bridge implementation is not compatible with version 3, cannot get message";
+    }
+  }
+  return "native bridge is not initialized";
+}
+
+bool NativeBridgeIsPathSupported(const char* path) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->isPathSupported(path);
+    } else {
+      ALOGE("not compatible with version %d, cannot check via library path", NAMESPACE_VERSION);
+    }
+  }
+  return false;
+}
+
+bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
+                                        const char* anon_ns_library_path) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path);
+    } else {
+      ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
+    }
+  }
+
+  return false;
+}
+
+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) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->createNamespace(name,
+                                        ld_library_path,
+                                        default_library_path,
+                                        type,
+                                        permitted_when_isolated_path,
+                                        parent_ns);
+    } else {
+      ALOGE("not compatible with version %d, cannot create namespace %s", NAMESPACE_VERSION, name);
+    }
+  }
+
+  return nullptr;
+}
+
+bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to,
+                                const char* shared_libs_sonames) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->linkNamespaces(from, to, shared_libs_sonames);
+    } else {
+      ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION);
+    }
+  }
+
+  return false;
+}
+
+native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
+  if (!NativeBridgeInitialized()) {
+    return nullptr;
+  }
+
+  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) {
+  if (NativeBridgeInitialized()) {
+    if (isCompatibleWith(NAMESPACE_VERSION)) {
+      return callbacks->loadLibraryExt(libpath, flag, ns);
+    } else {
+      ALOGE("not compatible with version %d, cannot load library in namespace", NAMESPACE_VERSION);
+    }
+  }
+  return nullptr;
+}
+
+}  // 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
new file mode 100644
index 0000000..2bb8467
--- /dev/null
+++ b/libnativebridge/tests/Android.bp
@@ -0,0 +1,110 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+    name: "libnativebridge-dummy-defaults",
+
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    header_libs: ["libnativebridge-headers"],
+    cppflags: ["-fvisibility=protected"],
+}
+
+cc_library_shared {
+    name: "libnativebridge-dummy",
+    srcs: ["DummyNativeBridge.cpp"],
+    defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+    name: "libnativebridge2-dummy",
+    srcs: ["DummyNativeBridge2.cpp"],
+    defaults: ["libnativebridge-dummy-defaults"],
+}
+
+cc_library_shared {
+    name: "libnativebridge3-dummy",
+    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/CodeCacheCreate_test.cpp b/libnativebridge/tests/CodeCacheCreate_test.cpp
new file mode 100644
index 0000000..58270c4
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheCreate_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace android {
+
+// Tests that the bridge initialization creates the code_cache if it doesn't
+// exists.
+TEST_F(NativeBridgeTest, CodeCacheCreate) {
+    // Make sure that code_cache does not exists
+    struct stat st;
+    ASSERT_EQ(-1, stat(kCodeCache, &st));
+    ASSERT_EQ(ENOENT, errno);
+
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_FALSE(NativeBridgeError());
+
+    // Check that code_cache was created
+    ASSERT_EQ(0, stat(kCodeCache, &st));
+    ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+    // Clean up
+    UnloadNativeBridge();
+    ASSERT_EQ(0, rmdir(kCodeCache));
+
+    ASSERT_FALSE(NativeBridgeError());
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/CodeCacheExists_test.cpp b/libnativebridge/tests/CodeCacheExists_test.cpp
new file mode 100644
index 0000000..8ba0158
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheExists_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache already
+// exists.
+TEST_F(NativeBridgeTest, CodeCacheExists) {
+    // Make sure that code_cache does not exists
+    struct stat st;
+    ASSERT_EQ(-1, stat(kCodeCache, &st));
+    ASSERT_EQ(ENOENT, errno);
+
+    // Create the code_cache
+    ASSERT_EQ(0, mkdir(kCodeCache, S_IRWXU | S_IRWXG | S_IXOTH));
+
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_FALSE(NativeBridgeError());
+
+    // Check that the code cache is still there
+    ASSERT_EQ(0, stat(kCodeCache, &st));
+    ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+    // Clean up
+    UnloadNativeBridge();
+    ASSERT_EQ(0, rmdir(kCodeCache));
+
+    ASSERT_FALSE(NativeBridgeError());
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp
new file mode 100644
index 0000000..4ea519e
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheStatFail_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache is
+// existed as a file.
+TEST_F(NativeBridgeTest, CodeCacheStatFail) {
+    int fd = creat(kCodeCache, O_RDWR);
+    ASSERT_NE(-1, fd);
+    close(fd);
+
+    struct stat st;
+    ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st));
+    ASSERT_EQ(ENOTDIR, errno);
+
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+    ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa"));
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_FALSE(NativeBridgeError());
+
+    // Clean up
+    UnloadNativeBridge();
+
+    ASSERT_FALSE(NativeBridgeError());
+    unlink(kCodeCache);
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/CompleteFlow_test.cpp b/libnativebridge/tests/CompleteFlow_test.cpp
new file mode 100644
index 0000000..b033792
--- /dev/null
+++ b/libnativebridge/tests/CompleteFlow_test.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+#include <unistd.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, CompleteFlow) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    // Basic calls to check that nothing crashes
+    ASSERT_FALSE(NativeBridgeIsSupported(nullptr));
+    ASSERT_EQ(nullptr, NativeBridgeLoadLibrary(nullptr, 0));
+    ASSERT_EQ(nullptr, NativeBridgeGetTrampoline(nullptr, nullptr, nullptr, 0));
+
+    // Unload
+    UnloadNativeBridge();
+
+    ASSERT_FALSE(NativeBridgeAvailable());
+    ASSERT_FALSE(NativeBridgeError());
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/DummyNativeBridge.cpp b/libnativebridge/tests/DummyNativeBridge.cpp
new file mode 100644
index 0000000..b9894f6
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+// A dummy implementation of the native-bridge interface.
+
+#include "nativebridge/native_bridge.h"
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+                                         const char* /* app_code_cache_dir */,
+                                         const char* /* isa */) {
+  return true;
+}
+
+extern "C" void* native_bridge_loadLibrary(const char* /* libpath */, int /* flag */) {
+  return nullptr;
+}
+
+extern "C" void* native_bridge_getTrampoline(void* /* handle */, const char* /* name */,
+                                             const char* /* shorty */, uint32_t /* len */) {
+  return nullptr;
+}
+
+extern "C" bool native_bridge_isSupported(const char* /* libpath */) {
+  return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge_getAppEnv(
+    const char* /* abi */) {
+  return nullptr;
+}
+
+android::NativeBridgeCallbacks NativeBridgeItf {
+  .version = 1,
+  .initialize = &native_bridge_initialize,
+  .loadLibrary = &native_bridge_loadLibrary,
+  .getTrampoline = &native_bridge_getTrampoline,
+  .isSupported = &native_bridge_isSupported,
+  .getAppEnv = &native_bridge_getAppEnv
+};
diff --git a/libnativebridge/tests/DummyNativeBridge2.cpp b/libnativebridge/tests/DummyNativeBridge2.cpp
new file mode 100644
index 0000000..6920c74
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge2.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+// A dummy implementation of the native-bridge interface.
+
+#include "nativebridge/native_bridge.h"
+
+#include <signal.h>
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge2_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+                                         const char* /* app_code_cache_dir */,
+                                         const char* /* isa */) {
+  return true;
+}
+
+extern "C" void* native_bridge2_loadLibrary(const char* /* libpath */, int /* flag */) {
+  return nullptr;
+}
+
+extern "C" void* native_bridge2_getTrampoline(void* /* handle */, const char* /* name */,
+                                             const char* /* shorty */, uint32_t /* len */) {
+  return nullptr;
+}
+
+extern "C" bool native_bridge2_isSupported(const char* /* libpath */) {
+  return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge2_getAppEnv(
+    const char* /* abi */) {
+  return nullptr;
+}
+
+extern "C" bool native_bridge2_is_compatible_compatible_with(uint32_t version) {
+  // For testing, allow 1 and 2, but disallow 3+.
+  return version <= 2;
+}
+
+static bool native_bridge2_dummy_signal_handler(int, siginfo_t*, void*) {
+  // TODO: Implement something here. We'd either have to have a death test with a log here, or
+  //       we'd have to be able to resume after the faulting instruction...
+  return true;
+}
+
+extern "C" android::NativeBridgeSignalHandlerFn native_bridge2_get_signal_handler(int signal) {
+  if (signal == SIGSEGV) {
+    return &native_bridge2_dummy_signal_handler;
+  }
+  return nullptr;
+}
+
+android::NativeBridgeCallbacks NativeBridgeItf {
+  .version = 2,
+  .initialize = &native_bridge2_initialize,
+  .loadLibrary = &native_bridge2_loadLibrary,
+  .getTrampoline = &native_bridge2_getTrampoline,
+  .isSupported = &native_bridge2_isSupported,
+  .getAppEnv = &native_bridge2_getAppEnv,
+  .isCompatibleWith = &native_bridge2_is_compatible_compatible_with,
+  .getSignalHandler = &native_bridge2_get_signal_handler
+};
+
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
new file mode 100644
index 0000000..4ef1c82
--- /dev/null
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+// A dummy implementation of the native-bridge interface.
+
+#include "nativebridge/native_bridge.h"
+
+#include <signal.h>
+
+// NativeBridgeCallbacks implementations
+extern "C" bool native_bridge3_initialize(
+                      const android::NativeBridgeRuntimeCallbacks* /* art_cbs */,
+                      const char* /* app_code_cache_dir */,
+                      const char* /* isa */) {
+  return true;
+}
+
+extern "C" void* native_bridge3_loadLibrary(const char* /* libpath */, int /* flag */) {
+  return nullptr;
+}
+
+extern "C" void* native_bridge3_getTrampoline(void* /* handle */, const char* /* name */,
+                                             const char* /* shorty */, uint32_t /* len */) {
+  return nullptr;
+}
+
+extern "C" bool native_bridge3_isSupported(const char* /* libpath */) {
+  return false;
+}
+
+extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge3_getAppEnv(
+    const char* /* abi */) {
+  return nullptr;
+}
+
+extern "C" bool native_bridge3_isCompatibleWith(uint32_t version) {
+  // For testing, allow 1-3, but disallow 4+.
+  return version <= 3;
+}
+
+static bool native_bridge3_dummy_signal_handler(int, siginfo_t*, void*) {
+  // TODO: Implement something here. We'd either have to have a death test with a log here, or
+  //       we'd have to be able to resume after the faulting instruction...
+  return true;
+}
+
+extern "C" android::NativeBridgeSignalHandlerFn native_bridge3_getSignalHandler(int signal) {
+  if (signal == SIGSEGV) {
+    return &native_bridge3_dummy_signal_handler;
+  }
+  return nullptr;
+}
+
+extern "C" int native_bridge3_unloadLibrary(void* /* handle */) {
+  return 0;
+}
+
+extern "C" const char* native_bridge3_getError() {
+  return nullptr;
+}
+
+extern "C" bool native_bridge3_isPathSupported(const char* /* path */) {
+  return true;
+}
+
+extern "C" bool native_bridge3_initAnonymousNamespace(const char* /* public_ns_sonames */,
+                                                      const char* /* anon_ns_library_path */) {
+  return true;
+}
+
+extern "C" android::native_bridge_namespace_t*
+native_bridge3_createNamespace(const char* /* name */,
+                               const char* /* ld_library_path */,
+                               const char* /* default_library_path */,
+                               uint64_t /* type */,
+                               const char* /* permitted_when_isolated_path */,
+                               android::native_bridge_namespace_t* /* parent_ns */) {
+  return nullptr;
+}
+
+extern "C" bool native_bridge3_linkNamespaces(android::native_bridge_namespace_t* /* from */,
+                                              android::native_bridge_namespace_t* /* to */,
+                                              const char* /* shared_libs_soname */) {
+  return true;
+}
+
+extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */,
+                                               int /* flag */,
+                                               android::native_bridge_namespace_t* /* ns */) {
+  return nullptr;
+}
+
+android::NativeBridgeCallbacks NativeBridgeItf{
+    // v1
+    .version = 3,
+    .initialize = &native_bridge3_initialize,
+    .loadLibrary = &native_bridge3_loadLibrary,
+    .getTrampoline = &native_bridge3_getTrampoline,
+    .isSupported = &native_bridge3_isSupported,
+    .getAppEnv = &native_bridge3_getAppEnv,
+    // v2
+    .isCompatibleWith = &native_bridge3_isCompatibleWith,
+    .getSignalHandler = &native_bridge3_getSignalHandler,
+    // v3
+    .unloadLibrary = &native_bridge3_unloadLibrary,
+    .getError = &native_bridge3_getError,
+    .isPathSupported = &native_bridge3_isPathSupported,
+    .initAnonymousNamespace = &native_bridge3_initAnonymousNamespace,
+    .createNamespace = &native_bridge3_createNamespace,
+    .linkNamespaces = &native_bridge3_linkNamespaces,
+    .loadLibraryExt = &native_bridge3_loadLibraryExt};
diff --git a/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
new file mode 100644
index 0000000..8f7973d
--- /dev/null
+++ b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+static const char* kTestName = "../librandom$@-bridge_not.existing.so";
+
+TEST_F(NativeBridgeTest, InvalidChars) {
+    // Do one test actually calling setup.
+    EXPECT_EQ(false, NativeBridgeError());
+    LoadNativeBridge(kTestName, nullptr);
+    // This should lead to an error for invalid characters.
+    EXPECT_EQ(true, NativeBridgeError());
+
+    // Further tests need to use NativeBridgeNameAcceptable, as the error
+    // state can't be changed back.
+    EXPECT_EQ(false, NativeBridgeNameAcceptable("."));
+    EXPECT_EQ(false, NativeBridgeNameAcceptable(".."));
+    EXPECT_EQ(false, NativeBridgeNameAcceptable("_"));
+    EXPECT_EQ(false, NativeBridgeNameAcceptable("-"));
+    EXPECT_EQ(false, NativeBridgeNameAcceptable("lib@.so"));
+    EXPECT_EQ(false, NativeBridgeNameAcceptable("lib$.so"));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge2Signal_test.cpp b/libnativebridge/tests/NativeBridge2Signal_test.cpp
new file mode 100644
index 0000000..44e45e3
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge2Signal_test.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+#include <signal.h>
+#include <unistd.h>
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
+
+TEST_F(NativeBridgeTest, V2_Signal) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary2, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(2U, NativeBridgeGetVersion());
+    ASSERT_NE(nullptr, NativeBridgeGetSignalHandler(SIGSEGV));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
new file mode 100644
index 0000000..668d942
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_CreateNamespace) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(nullptr, NativeBridgeCreateNamespace(nullptr, nullptr, nullptr,
+                                                   0, nullptr, nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3GetError_test.cpp b/libnativebridge/tests/NativeBridge3GetError_test.cpp
new file mode 100644
index 0000000..0b9f582
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3GetError_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_GetError) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(nullptr, NativeBridgeGetError());
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
new file mode 100644
index 0000000..b0d6b09
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) {
+  // Init
+  ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+  ASSERT_TRUE(NativeBridgeAvailable());
+  ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+  ASSERT_TRUE(NativeBridgeAvailable());
+  ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+  ASSERT_TRUE(NativeBridgeAvailable());
+
+  ASSERT_EQ(3U, NativeBridgeGetVersion());
+  ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr));
+
+  // Clean-up code_cache
+  ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
new file mode 100644
index 0000000..325e40b
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_IsPathSupported) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(true, NativeBridgeIsPathSupported(nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
new file mode 100644
index 0000000..4caeb44
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_LoadLibraryExt) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(nullptr, NativeBridgeLoadLibraryExt(nullptr, 0, nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
new file mode 100644
index 0000000..93a979c
--- /dev/null
+++ b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+namespace android {
+
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+TEST_F(NativeBridgeTest, V3_UnloadLibrary) {
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(PreInitializeNativeBridge(".", "isa"));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+
+    ASSERT_EQ(3U, NativeBridgeGetVersion());
+    ASSERT_EQ(0, NativeBridgeUnloadLibrary(nullptr));
+
+    // Clean-up code_cache
+    ASSERT_EQ(0, rmdir(kCodeCache));
+}
+
+}  // namespace android
diff --git a/libmodprobe/libmodprobe_test.h b/libnativebridge/tests/NativeBridgeApi.c
similarity index 70%
copy from libmodprobe/libmodprobe_test.h
copy to libnativebridge/tests/NativeBridgeApi.c
index a001b69..7ab71fe 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/libnativebridge/tests/NativeBridgeApi.c
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-#pragma once
+/* 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"
 
-#include <string>
-#include <vector>
-
-extern std::vector<std::string> test_modules;
-extern std::vector<std::string> modules_loaded;
+int main(int argc, char** argv) {
+  (void)argc;
+  (void)argv;
+  return 0;
+}
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
new file mode 100644
index 0000000..0f99816
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -0,0 +1,39 @@
+/*
+ * 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 NATIVE_BRIDGE_TEST_H_
+#define NATIVE_BRIDGE_TEST_H_
+
+#define LOG_TAG "NativeBridge_test"
+
+#include <nativebridge/native_bridge.h>
+#include <gtest/gtest.h>
+
+constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
+constexpr const char* kCodeCache = "./code_cache";
+constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
+constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so";
+constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so";
+
+namespace android {
+
+class NativeBridgeTest : public testing::Test {
+};
+
+};  // namespace android
+
+#endif  // NATIVE_BRIDGE_H_
+
diff --git a/libnativebridge/tests/NativeBridgeVersion_test.cpp b/libnativebridge/tests/NativeBridgeVersion_test.cpp
new file mode 100644
index 0000000..d3f9a80
--- /dev/null
+++ b/libnativebridge/tests/NativeBridgeVersion_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+#include <unistd.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, Version) {
+    // When a bridge isn't loaded, we expect 0.
+    EXPECT_EQ(NativeBridgeGetVersion(), 0U);
+
+    // After our dummy bridge has been loaded, we expect 1.
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+    EXPECT_EQ(NativeBridgeGetVersion(), 1U);
+
+    // Unload
+    UnloadNativeBridge();
+
+    // Version information is gone.
+    EXPECT_EQ(NativeBridgeGetVersion(), 0U);
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
new file mode 100644
index 0000000..c8ff743
--- /dev/null
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+#include <android-base/macros.h>
+
+namespace android {
+
+static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
+                               "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
+
+TEST_F(NativeBridgeTest, NeedsNativeBridge) {
+  EXPECT_EQ(false, NeedsNativeBridge(ABI_STRING));
+
+  const size_t kISACount = sizeof(kISAs) / sizeof(kISAs[0]);
+  for (size_t i = 0; i < kISACount; i++) {
+    EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], ABI_STRING) != 0,
+              NeedsNativeBridge(kISAs[i]));
+    }
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
new file mode 100644
index 0000000..5a2b0a1
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "NativeBridgeTest.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
+namespace android {
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail1) {
+  // Needs a valid application directory.
+  ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+  ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa"));
+  ASSERT_TRUE(NativeBridgeError());
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
new file mode 100644
index 0000000..af976b1
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail2) {
+  // Needs LoadNativeBridge() first
+  ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa"));
+  ASSERT_TRUE(NativeBridgeError());
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
new file mode 100644
index 0000000..cd5a8e2
--- /dev/null
+++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
@@ -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.
+ */
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include <android/log.h>
+
+#include "NativeBridgeTest.h"
+
+namespace android {
+
+TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+#if !defined(__APPLE__)         // Mac OS does not support bind-mount.
+#if !defined(__ANDROID__)       // Cannot write into the hard-wired location.
+    static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
+
+    // Try to create our mount namespace.
+    if (unshare(CLONE_NEWNS) != -1) {
+        // Create a dummy file.
+        FILE* cpuinfo = fopen("./cpuinfo", "w");
+        ASSERT_NE(nullptr, cpuinfo) << strerror(errno);
+        fprintf(cpuinfo, kTestData);
+        fclose(cpuinfo);
+
+        ASSERT_TRUE(PreInitializeNativeBridge("does not matter 1", "short 2"));
+
+        // Read /proc/cpuinfo
+        FILE* proc_cpuinfo = fopen("/proc/cpuinfo", "r");
+        ASSERT_NE(nullptr, proc_cpuinfo) << strerror(errno);
+        char buf[1024];
+        EXPECT_NE(nullptr, fgets(buf, sizeof(buf), proc_cpuinfo)) << "Error reading.";
+        fclose(proc_cpuinfo);
+
+        EXPECT_EQ(0, strcmp(buf, kTestData));
+
+        // Delete the file.
+        ASSERT_EQ(0, unlink("./cpuinfo")) << "Error unlinking temporary file.";
+        // Ending the test will tear down the mount namespace.
+    } else {
+        GTEST_LOG_(WARNING) << "Could not create mount namespace. Are you running this as root?";
+    }
+#endif
+#endif
+}
+
+}  // namespace android
diff --git a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
similarity index 60%
copy from fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
copy to libnativebridge/tests/ReSetupNativeBridge_test.cpp
index ab7ba73..944e5d7 100644
--- a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
+++ b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
-#pragma once
-
-#include <map>
-#include <string>
+#include "NativeBridgeTest.h"
 
 namespace android {
-namespace fs_mgr {
 
-bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
-                            const std::map<std::string, std::string>& images_path);
+TEST_F(NativeBridgeTest, ReSetup) {
+    EXPECT_EQ(false, NativeBridgeError());
+    LoadNativeBridge("", nullptr);
+    EXPECT_EQ(false, NativeBridgeError());
+    LoadNativeBridge("", nullptr);
+    // This should lead to an error for trying to re-setup a native bridge.
+    EXPECT_EQ(true, NativeBridgeError());
+}
 
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
similarity index 60%
copy from fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
copy to libnativebridge/tests/UnavailableNativeBridge_test.cpp
index ab7ba73..ad374a5 100644
--- a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
+++ b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-#pragma once
-
-#include <map>
-#include <string>
+#include "NativeBridgeTest.h"
 
 namespace android {
-namespace fs_mgr {
 
-bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
-                            const std::map<std::string, std::string>& images_path);
+TEST_F(NativeBridgeTest, NoNativeBridge) {
+    EXPECT_EQ(false, NativeBridgeAvailable());
+    // Try to initialize. This should fail as we are not set up.
+    EXPECT_EQ(false, InitializeNativeBridge(nullptr, nullptr));
+    EXPECT_EQ(true, NativeBridgeError());
+    EXPECT_EQ(false, NativeBridgeAvailable());
+}
 
-}  // namespace fs_mgr
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/libnativebridge/tests/ValidNameNativeBridge_test.cpp b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
new file mode 100644
index 0000000..690be4a
--- /dev/null
+++ b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <NativeBridgeTest.h>
+
+namespace android {
+
+static const char* kTestName = "librandom-bridge_not.existing.so";
+
+TEST_F(NativeBridgeTest, ValidName) {
+    // Check that the name is acceptable.
+    EXPECT_EQ(true, NativeBridgeNameAcceptable(kTestName));
+
+    // Now check what happens on LoadNativeBridge.
+    EXPECT_EQ(false, NativeBridgeError());
+    LoadNativeBridge(kTestName, nullptr);
+    // This will lead to an error as the library doesn't exist.
+    EXPECT_EQ(true, NativeBridgeError());
+    EXPECT_EQ(false, NativeBridgeAvailable());
+}
+
+}  // namespace android
diff --git a/libpackagelistparser/.clang-format b/libnativeloader/.clang-format
similarity index 100%
rename from libpackagelistparser/.clang-format
rename to libnativeloader/.clang-format
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
new file mode 100644
index 0000000..66cb49f
--- /dev/null
+++ b/libnativeloader/Android.bp
@@ -0,0 +1,65 @@
+// Shared library for target
+// ========================================================
+cc_defaults {
+    name: "libnativeloader-defaults",
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    cppflags: [
+        "-fvisibility=hidden",
+    ],
+    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: {
+            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
new file mode 100644
index 0000000..f735653
--- /dev/null
+++ b/libnativeloader/OWNERS
@@ -0,0 +1,6 @@
+dimitry@google.com
+jiyong@google.com
+ngeoffray@google.com
+oth@google.com
+mast@google.com
+rpl@google.com
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
new file mode 100644
index 0000000..2d6ce85
--- /dev/null
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -0,0 +1,116 @@
+/*
+ * 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 __ANDROID_DLEXT_NAMESPACES_H__
+#define __ANDROID_DLEXT_NAMESPACES_H__
+
+#include <android/dlext.h>
+#include <stdbool.h>
+
+__BEGIN_DECLS
+
+/*
+ * Initializes anonymous namespaces. The shared_libs_sonames is the list of sonames
+ * to be shared by default namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
+ *
+ * The library_search_path is the search path for anonymous namespace. The anonymous namespace
+ * is used in the case when linker cannot identify the caller of dlopen/dlsym. This happens
+ * for the code not loaded by dynamic linker; for example calls from the mono-compiled code.
+ */
+extern bool android_init_anonymous_namespace(const char* shared_libs_sonames,
+                                             const char* library_search_path);
+
+
+enum {
+  /* A regular namespace is the namespace with a custom search path that does
+   * not impose any restrictions on the location of native libraries.
+   */
+  ANDROID_NAMESPACE_TYPE_REGULAR = 0,
+
+  /* An isolated namespace requires all the libraries to be on the search path
+   * or under permitted_when_isolated_path. The search path is the union of
+   * ld_library_path and default_library_path.
+   */
+  ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+
+  /* The shared namespace clones the list of libraries of the caller namespace upon creation
+   * which means that they are shared between namespaces - the caller namespace and the new one
+   * will use the same copy of a library if it was loaded prior to android_create_namespace call.
+   *
+   * Note that libraries loaded after the namespace is created will not be shared.
+   *
+   * Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor
+   * permitted_path from the caller's namespace.
+   */
+  ANDROID_NAMESPACE_TYPE_SHARED = 2,
+
+  /* This flag instructs linker to enable grey-list workaround for the namespace.
+   * See http://b/26394120 for details.
+   */
+  ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED = 0x08000000,
+
+  ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED |
+                                           ANDROID_NAMESPACE_TYPE_ISOLATED,
+};
+
+/*
+ * Creates new linker namespace.
+ * ld_library_path and default_library_path represent the search path
+ * for the libraries in the namespace.
+ *
+ * The libraries in the namespace are searched by folowing order:
+ * 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH)
+ * 2. In directories specified by DT_RUNPATH of the "needed by" binary.
+ * 3. deault_library_path (This of this as namespace-local default library path)
+ *
+ * When type is ANDROID_NAMESPACE_TYPE_ISOLATED the resulting namespace requires all of
+ * the libraries to be on the search path or under the permitted_when_isolated_path;
+ * the search_path is ld_library_path:default_library_path. Note that the
+ * permitted_when_isolated_path path is not part of the search_path and
+ * does not affect the search order. It is a way to allow loading libraries from specific
+ * locations when using absolute path.
+ * 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, struct android_namespace_t* parent);
+
+/*
+ * Creates a link between namespaces. Every link has list of sonames of
+ * shared libraries. These are the libraries which are accessible from
+ * namespace 'from' but loaded within namespace 'to' context.
+ * When to namespace is nullptr this function establishes a link between
+ * 'from' namespace and the default namespace.
+ *
+ * The lookup order of the libraries in namespaces with links is following:
+ * 1. Look inside current namespace using 'this' namespace search path.
+ * 2. Look in linked namespaces
+ * 2.1. Perform soname check - if library soname is not in the list of shared
+ *      libraries sonames skip this link, otherwise
+ * 2.2. Search library using linked namespace search path. Note that this
+ *      step will not go deeper into linked namespaces for this library but
+ *      will do so for DT_NEEDED libraries.
+ */
+extern bool android_link_namespaces(struct android_namespace_t* from,
+                                    struct android_namespace_t* to,
+                                    const char* shared_libs_sonames);
+
+extern struct android_namespace_t* android_get_exported_namespace(const char* name);
+
+__END_DECLS
+
+#endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
new file mode 100644
index 0000000..51fb875
--- /dev/null
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -0,0 +1,78 @@
+/*
+ * 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 NATIVE_LOADER_H_
+#define NATIVE_LOADER_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#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, 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,
+    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,
+                                                               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.
+// 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();
+
+#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/native_loader.cpp b/libnativeloader/native_loader.cpp
new file mode 100644
index 0000000..e460b1a
--- /dev/null
+++ b/libnativeloader/native_loader.cpp
@@ -0,0 +1,952 @@
+/*
+ * 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 "nativeloader/native_loader.h"
+#include <nativehelper/ScopedUtfChars.h>
+
+#include <dlfcn.h>
+#ifdef __ANDROID__
+#define LOG_TAG "libnativeloader"
+#include "nativeloader/dlext_namespaces.h"
+#include "log/log.h"
+#endif
+#include <dirent.h>
+#include <sys/types.h>
+#include "nativebridge/native_bridge.h"
+
+#include <algorithm>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <regex>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+
+#ifdef __BIONIC__
+#include <android-base/properties.h>
+#endif
+
+#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\
+                                             "%s:%d: %s CHECK '" #predicate "' failed.",\
+                                             __FILE__, __LINE__, __FUNCTION__)
+
+using namespace std::string_literals;
+
+namespace android {
+
+#if defined(__ANDROID__)
+struct NativeLoaderNamespace {
+ public:
+  NativeLoaderNamespace()
+      : android_ns_(nullptr), native_bridge_ns_(nullptr) { }
+
+  explicit NativeLoaderNamespace(android_namespace_t* ns)
+      : android_ns_(ns), native_bridge_ns_(nullptr) { }
+
+  explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
+      : android_ns_(nullptr), native_bridge_ns_(ns) { }
+
+  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_;
+  }
+
+  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";
+
+static const std::vector<const std::string> kRuntimePublicLibraries = {
+    "libicuuc.so",
+    "libicui18n.so",
+};
+
+// 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* kDefaultNamespaceName = "default";
+static constexpr const char* kPlatformNamespaceName = "platform";
+static 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
+// default 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.
+static constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
+// Same thing for vendor APKs.
+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 constexpr const char* kApexPath = "/apex/";
+
+#if defined(__LP64__)
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib64";
+static constexpr const char* kVendorLibPath = "/vendor/lib64";
+static constexpr const char* kProductLibPath = "/product/lib64:/system/product/lib64";
+#else
+static constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/lib";
+static constexpr const char* kVendorLibPath = "/vendor/lib";
+static constexpr const char* kProductLibPath = "/product/lib:/system/product/lib";
+#endif
+
+static const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+static 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;
+
+static bool is_debuggable() {
+  bool debuggable = false;
+#ifdef __BIONIC__
+  debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+#endif
+  return debuggable;
+}
+
+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) { }
+
+  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) {
+    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");
+
+    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 = FindParentNamespaceByClassLoader(env, class_loader);
+
+    bool is_native_bridge = false;
+
+    if (parent_ns != nullptr) {
+      is_native_bridge = !parent_ns->is_android_namespace();
+    } else if (!library_path.empty()) {
+      is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
+    }
+
+    std::string system_exposed_libraries = system_public_libraries_;
+    const char* namespace_name = kClassloaderNamespaceName;
+    android_namespace_t* vndk_ns = nullptr;
+    if ((apk_origin == APK_ORIGIN_VENDOR ||
+         (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
+        !is_shared) {
+      LOG_FATAL_IF(is_native_bridge,
+                   "Unbundled vendor / product apk must not use translated architecture");
+
+      // 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 = "";
+      }
+
+      LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
+                   origin_partition);
+
+      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 + ":" + system_llndk_libraries_.c_str();
+
+      // Give access to VNDK-SP libraries from the 'vndk' namespace.
+      vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
+      if (vndk_ns == nullptr) {
+        ALOGW("Cannot find \"%s\" namespace for %s apks", kVndkNamespaceName, origin_partition);
+      }
+
+      // 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 {
+      // 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_;
+      }
+    }
+
+    std::string runtime_exposed_libraries = base::Join(kRuntimePublicLibraries, ":");
+
+    NativeLoaderNamespace native_loader_ns;
+    if (!is_native_bridge) {
+      android_namespace_t* android_parent_ns;
+      if (parent_ns != nullptr) {
+        android_parent_ns = parent_ns->get_android_ns();
+      } else {
+        // Fall back to the platform namespace if no parent is found. It is
+        // called "default" for binaries in /system and "platform" for those in
+        // the Runtime APEX. Try "platform" first since "default" always exists.
+        android_parent_ns = android_get_exported_namespace(kPlatformNamespaceName);
+        if (android_parent_ns == nullptr) {
+          android_parent_ns = android_get_exported_namespace(kDefaultNamespaceName);
+        }
+      }
+
+      android_namespace_t* ns = android_create_namespace(namespace_name,
+                                                         nullptr,
+                                                         library_path.c_str(),
+                                                         namespace_type,
+                                                         permitted_path.c_str(),
+                                                         android_parent_ns);
+      if (ns == nullptr) {
+        *error_msg = dlerror();
+        return nullptr;
+      }
+
+      // 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);
+
+      android_namespace_t* runtime_ns = android_get_exported_namespace(kRuntimeNamespaceName);
+
+      if (!android_link_namespaces(ns, nullptr, system_exposed_libraries.c_str())) {
+        *error_msg = dlerror();
+        return nullptr;
+      }
+
+      // Runtime apex does not exist in host, and under certain build conditions.
+      if (runtime_ns != nullptr) {
+        if (!android_link_namespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
+          *error_msg = dlerror();
+          return nullptr;
+        }
+      }
+
+      if (vndk_ns != nullptr && !system_vndksp_libraries_.empty()) {
+        // vendor apks are allowed to use VNDK-SP libraries.
+        if (!android_link_namespaces(ns, vndk_ns, system_vndksp_libraries_.c_str())) {
+          *error_msg = dlerror();
+          return nullptr;
+        }
+      }
+
+      if (!vendor_public_libraries_.empty()) {
+        if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+          *error_msg = dlerror();
+          return nullptr;
+        }
+      }
+
+      native_loader_ns = NativeLoaderNamespace(ns);
+    } else {
+      native_bridge_namespace_t* native_bridge_parent_namespace;
+      if (parent_ns != nullptr) {
+        native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
+      } else {
+        native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
+        if (native_bridge_parent_namespace == nullptr) {
+          native_bridge_parent_namespace = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
+        }
+      }
+
+      native_bridge_namespace_t* ns = NativeBridgeCreateNamespace(namespace_name,
+                                                                  nullptr,
+                                                                  library_path.c_str(),
+                                                                  namespace_type,
+                                                                  permitted_path.c_str(),
+                                                                  native_bridge_parent_namespace);
+      if (ns == nullptr) {
+        *error_msg = NativeBridgeGetError();
+        return nullptr;
+      }
+
+      native_bridge_namespace_t* vendor_ns = NativeBridgeGetExportedNamespace(kVendorNamespaceName);
+      native_bridge_namespace_t* runtime_ns =
+          NativeBridgeGetExportedNamespace(kRuntimeNamespaceName);
+
+      if (!NativeBridgeLinkNamespaces(ns, nullptr, system_exposed_libraries.c_str())) {
+        *error_msg = NativeBridgeGetError();
+        return nullptr;
+      }
+
+      // Runtime apex does not exist in host, and under certain build conditions.
+      if (runtime_ns != nullptr) {
+        if (!NativeBridgeLinkNamespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
+          *error_msg = NativeBridgeGetError();
+          return nullptr;
+        }
+      }
+      if (!vendor_public_libraries_.empty()) {
+        if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries_.c_str())) {
+          *error_msg = NativeBridgeGetError();
+          return nullptr;
+        }
+      }
+
+      native_loader_ns = NativeLoaderNamespace(ns);
+    }
+
+    namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
+
+    return &(namespaces_.back().second);
+  }
+
+  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+    auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+                [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
+                  return env->IsSameObject(value.first, class_loader);
+                });
+    if (it != namespaces_.end()) {
+      return &it->second;
+    }
+
+    return nullptr;
+  }
+
+  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));
+      }
+    }
+
+    // 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
+    removePublicLibsIfExistsInRuntimeApex(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());
+        }
+      }
+    }
+  }
+
+  /**
+   * Remove the public libs in runtime namespace
+   */
+  void removePublicLibsIfExistsInRuntimeApex(std::vector<std::string>& sonames) {
+    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);
+      }
+    }
+  }
+
+  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);
+  }
+
+  NativeLoaderNamespace* 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;
+  }
+
+  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;
+  }
+
+  bool initialized_;
+  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
+  std::string system_public_libraries_;
+  std::string vendor_public_libraries_;
+  std::string oem_public_libraries_;
+  std::string product_public_libraries_;
+  std::string system_llndk_libraries_;
+  std::string system_vndksp_libraries_;
+
+  DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
+};
+
+static std::mutex g_namespaces_mutex;
+static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
+#endif
+
+void InitializeNativeLoader() {
+#if defined(__ANDROID__)
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  g_namespaces->Initialize();
+#endif
+}
+
+void ResetNativeLoader() {
+#if defined(__ANDROID__)
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  g_namespaces->Reset();
+#endif
+}
+
+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;
+  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, dex_path, library_path, permitted_path);
+#endif
+  return nullptr;
+}
+
+#if defined(__ANDROID__)
+static 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;
+  }
+  return nullptr;
+}
+#endif
+
+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;
+    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;
+
+  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.
+    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;
+    }
+  }
+
+  return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);
+#else
+  UNUSED(env, target_sdk_version, class_loader, caller_location);
+
+  // Do some best effort to emulate library-path support. It will not
+  // work for dependencies.
+  //
+  // Note: null has a special meaning and must be preserved.
+  std::string c_library_path;  // Empty string by default.
+  if (library_path != nullptr && path != nullptr && path[0] != '/') {
+    ScopedUtfChars library_path_utf_chars(env, library_path);
+    c_library_path = library_path_utf_chars.c_str();
+  }
+
+  std::vector<std::string> library_paths = base::Split(c_library_path, ":");
+
+  for (const std::string& lib_path : library_paths) {
+    *needs_native_bridge = false;
+    const char* path_arg;
+    std::string complete_path;
+    if (path == nullptr) {
+      // Preserve null.
+      path_arg = nullptr;
+    } else {
+      complete_path = lib_path;
+      if (!complete_path.empty()) {
+        complete_path.append("/");
+      }
+      complete_path.append(path);
+      path_arg = complete_path.c_str();
+    }
+    void* handle = dlopen(path_arg, RTLD_NOW);
+    if (handle != nullptr) {
+      return handle;
+    }
+    if (NativeBridgeIsSupported(path_arg)) {
+      *needs_native_bridge = true;
+      handle = NativeBridgeLoadLibrary(path_arg, RTLD_NOW);
+      if (handle != nullptr) {
+        return handle;
+      }
+      *error_msg = strdup(NativeBridgeGetError());
+    } else {
+      *error_msg = strdup(dlerror());
+    }
+  }
+  return nullptr;
+#endif
+}
+
+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) {
+  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 = strdup(dlerror());
+    }
+    *needs_native_bridge = false;
+    return handle;
+  } else {
+    void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
+    if (handle == nullptr) {
+      *error_msg = strdup(NativeBridgeGetError());
+    }
+    *needs_native_bridge = true;
+    return handle;
+  }
+}
+
+// native_bridge_namespaces are not supported for callers of this function.
+// This function will return nullptr in the case when application is running
+// on native bridge.
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+  if (ns != nullptr) {
+    return ns->is_android_namespace() ? ns->get_android_ns() : nullptr;
+  }
+
+  return nullptr;
+}
+NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
+  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
+#endif
+
+}; //  android namespace
diff --git a/libnativeloader/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/test/Android.bp b/libnativeloader/test/Android.bp
new file mode 100644
index 0000000..4d5c53d
--- /dev/null
+++ b/libnativeloader/test/Android.bp
@@ -0,0 +1,82 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library {
+    name: "libfoo.oem1",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libfoo.oem1.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libbar.oem1",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libbar.oem1.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libfoo.oem2",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libfoo.oem2.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libbar.oem2",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libbar.oem2.so\""],
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libfoo.product1",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libfoo.product1.so\""],
+    product_specific: true,
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+cc_library {
+    name: "libbar.product1",
+    srcs: ["test.cpp"],
+    cflags: ["-DLIBNAME=\"libbar.product1.so\""],
+    product_specific: true,
+    shared_libs: [
+        "libbase",
+    ],
+}
+
+// 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/Android.mk b/libnativeloader/test/Android.mk
new file mode 100644
index 0000000..65e7b09
--- /dev/null
+++ b/libnativeloader/test/Android.mk
@@ -0,0 +1,57 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-oem1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-oem2.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := public.libraries-product1.txt
+LOCAL_SRC_FILES:= $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := oemlibrarytest-system
+LOCAL_MODULE_TAGS := tests
+LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_MODULE_PATH := $(TARGET_OUT_APPS)
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := oemlibrarytest-vendor
+LOCAL_MODULE_TAGS := tests
+LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_APPS)
+include $(BUILD_PACKAGE)
diff --git a/libmodprobe/libmodprobe_test.h b/libnativeloader/test/api_test.c
similarity index 70%
copy from libmodprobe/libmodprobe_test.h
copy to libnativeloader/test/api_test.c
index a001b69..e7025fd 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/libnativeloader/test/api_test.c
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-#pragma once
+/* 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"
 
-#include <string>
-#include <vector>
-
-extern std::vector<std::string> test_modules;
-extern std::vector<std::string> modules_loaded;
+int main(int argc, char** argv) {
+  (void)argc;
+  (void)argv;
+  return 0;
+}
diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt
new file mode 100644
index 0000000..f9433e2
--- /dev/null
+++ b/libnativeloader/test/public.libraries-oem1.txt
@@ -0,0 +1,2 @@
+libfoo.oem1.so
+libbar.oem1.so
diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt
new file mode 100644
index 0000000..de6bdb0
--- /dev/null
+++ b/libnativeloader/test/public.libraries-oem2.txt
@@ -0,0 +1,2 @@
+libfoo.oem2.so
+libbar.oem2.so
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
new file mode 100644
index 0000000..358154c
--- /dev/null
+++ b/libnativeloader/test/public.libraries-product1.txt
@@ -0,0 +1,2 @@
+libfoo.product1.so
+libbar.product1.so
diff --git a/libnativeloader/test/runtest.sh b/libnativeloader/test/runtest.sh
new file mode 100755
index 0000000..40beb5b
--- /dev/null
+++ b/libnativeloader/test/runtest.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+adb root
+adb remount
+adb sync
+adb shell stop
+adb shell start
+sleep 5 # wait until device reboots
+adb logcat -c;
+adb shell am start -n android.test.app.system/android.test.app.TestActivity
+adb shell am start -n android.test.app.vendor/android.test.app.TestActivity
+adb logcat | grep android.test.app
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
new file mode 100644
index 0000000..a7a455d
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/TestActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle icicle) {
+         super.onCreate(icicle);
+         tryLoadingLib("foo.oem1");
+         tryLoadingLib("bar.oem1");
+         tryLoadingLib("foo.oem2");
+         tryLoadingLib("bar.oem2");
+         tryLoadingLib("foo.product1");
+         tryLoadingLib("bar.product1");
+    }
+
+    private void tryLoadingLib(String name) {
+        try {
+            System.loadLibrary(name);
+            Log.d(getPackageName(), "library " + name + " is successfully loaded");
+        } catch (UnsatisfiedLinkError e) {
+            Log.d(getPackageName(), "failed to load libarary " + name, e);
+        }
+    }
+}
diff --git a/libnativeloader/test/system/AndroidManifest.xml b/libnativeloader/test/system/AndroidManifest.xml
new file mode 100644
index 0000000..c304889
--- /dev/null
+++ b/libnativeloader/test/system/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.test.app.system">
+
+    <application>
+        <activity android:name="android.test.app.TestActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
+
diff --git a/libmodprobe/libmodprobe_test.h b/libnativeloader/test/test.cpp
similarity index 72%
copy from libmodprobe/libmodprobe_test.h
copy to libnativeloader/test/test.cpp
index a001b69..b166928 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/libnativeloader/test/test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 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.
@@ -13,11 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "oemlib"
+#include <android-base/logging.h>
 
-#pragma once
-
-#include <string>
-#include <vector>
-
-extern std::vector<std::string> test_modules;
-extern std::vector<std::string> modules_loaded;
+static __attribute__((constructor)) void test_lib_init() {
+  LOG(DEBUG) << LIBNAME << " loaded";
+}
diff --git a/libnativeloader/test/vendor/AndroidManifest.xml b/libnativeloader/test/vendor/AndroidManifest.xml
new file mode 100644
index 0000000..c4c1a9c
--- /dev/null
+++ b/libnativeloader/test/vendor/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.test.app.vendor">
+
+    <application>
+        <activity android:name="android.test.app.TestActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
+
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 6af49bb..a098d59 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -362,12 +362,10 @@
     return err->error;
 }
 
-// Returns zero on success and negative errno on failure.
 int ifc_add_address(const char *name, const char *address, int prefixlen) {
     return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen);
 }
 
-// Returns zero on success and negative errno on failure.
 int ifc_del_address(const char *name, const char * address, int prefixlen) {
     return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen);
 }
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index b56dcdb..c38594a 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,7 +1,12 @@
 cc_library {
+
     name: "libpackagelistparser",
     recovery_available: true,
-    srcs: ["packagelistparser.cpp"],
+    srcs: ["packagelistparser.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: ["liblog"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
@@ -10,14 +15,3 @@
         misc_undefined: ["integer"],
     },
 }
-
-cc_test {
-    name: "libpackagelistparser_test",
-    srcs: ["packagelistparser_test.cpp"],
-    shared_libs: [
-        "libbase",
-        "libpackagelistparser",
-    ],
-    test_suites: ["device-tests"],
-    require_root: true,
-}
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
index e89cb54..3cb6b9a 100644
--- a/libpackagelistparser/include/packagelistparser/packagelistparser.h
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -1,81 +1,94 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2015, Intel Corporation
+ * 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
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ * This is a parser library for parsing the packages.list file generated
+ * by PackageManager service.
+ *
+ * This simple parser is sensitive to format changes in
+ * frameworks/base/services/core/java/com/android/server/pm/Settings.java
+ * A dependency note has been added to that file to correct
+ * this parser.
  */
 
-#pragma once
+#ifndef PACKAGELISTPARSER_H_
+#define PACKAGELISTPARSER_H_
 
 #include <stdbool.h>
+#include <sys/cdefs.h>
 #include <sys/types.h>
 
 __BEGIN_DECLS
 
-typedef struct gid_list {
-  /** Number of gids. */
-  size_t cnt;
+/** The file containing the list of installed packages on the system */
+#define PACKAGES_LIST_FILE  "/data/system/packages.list"
 
-  /** Array of gids. */
-  gid_t* gids;
-} gid_list;
+typedef struct pkg_info pkg_info;
+typedef struct gid_list gid_list;
 
-typedef struct pkg_info {
-  /** Package name like "com.android.blah". */
-  char* name;
+struct gid_list {
+    size_t cnt;
+    gid_t *gids;
+};
 
-  /** Package uid like 10014. */
-  uid_t uid;
-
-  /** Package's AndroidManifest.xml debuggable flag. */
-  bool debuggable;
-
-  /** Package data directory like "/data/user/0/com.android.blah" */
-  char* data_dir;
-
-  /** Package SELinux info. */
-  char* seinfo;
-
-  /** Package's list of gids. */
-  gid_list gids;
-
-  /** Spare pointer for the caller to stash extra data off. */
-  void* private_data;
-
-  /** Package's AndroidManifest.xml profileable flag. */
-  bool profileable_from_shell;
-
-  /** Package's AndroidManifest.xml version code. */
-  long version_code;
-} pkg_info;
+struct pkg_info {
+    char *name;
+    uid_t uid;
+    bool debuggable;
+    char *data_dir;
+    char *seinfo;
+    gid_list gids;
+    void *private_data;
+    bool profileable_from_shell;
+    long version_code;
+};
 
 /**
- * Parses the system's default package list.
- * Invokes `callback` once for each package.
- * The callback owns the `pkg_info*` and should call packagelist_free().
- * The callback should return `false` to exit early or `true` to continue.
+ * Callback function to be used by packagelist_parse() routine.
+ * @param info
+ *  The parsed package information
+ * @param userdata
+ *  The supplied userdata pointer to packagelist_parse()
+ * @return
+ *  true to keep processing, false to stop.
  */
-bool packagelist_parse(bool (*callback)(pkg_info* info, void* user_data), void* user_data);
+typedef bool (*pfn_on_package)(pkg_info *info, void *userdata);
 
 /**
- * Parses the given package list.
- * Invokes `callback` once for each package.
- * The callback owns the `pkg_info*` and should call packagelist_free().
- * The callback should return `false` to exit early or `true` to continue.
+ * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on
+ * each entry found. Once the callback is invoked, ownership of the pkg_info pointer
+ * is passed to the callback routine, thus they are required to perform any cleanup
+ * desired.
+ * @param callback
+ *  The callback function called on each parsed line of the packages list.
+ * @param userdata
+ *  An optional userdata supplied pointer to pass to the callback function.
+ * @return
+ *  true on success false on failure.
  */
-bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info* info, void* user_data),
-                            void* user_data);
+extern bool packagelist_parse(pfn_on_package callback, void *userdata);
 
-/** Frees the given `pkg_info`. */
-void packagelist_free(pkg_info* info);
+/**
+ * Frees a pkg_info structure.
+ * @param info
+ *  The struct to free
+ */
+extern void packagelist_free(pkg_info *info);
 
 __END_DECLS
+
+#endif /* PACKAGELISTPARSER_H_ */
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
new file mode 100644
index 0000000..edc533c
--- /dev/null
+++ b/libpackagelistparser/packagelistparser.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * 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.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ */
+
+#define LOG_TAG "packagelistparser"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/limits.h>
+
+#include <log/log.h>
+#include <packagelistparser/packagelistparser.h>
+
+#define CLOGE(fmt, ...) \
+    do {\
+        IF_ALOGE() {\
+            ALOGE(fmt, ##__VA_ARGS__);\
+        }\
+    } while(0)
+
+static size_t get_gid_cnt(const char *gids)
+{
+    size_t cnt;
+
+    if (*gids == '\0') {
+        return 0;
+    }
+
+    if (!strcmp(gids, "none")) {
+        return 0;
+    }
+
+    for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
+        ;
+
+    return cnt;
+}
+
+static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
+{
+    gid_t gid;
+    char* token;
+    char *endptr;
+    size_t cmp = 0;
+
+    while ((token = strsep(&gids, ",\r\n"))) {
+
+        if (cmp > *cnt) {
+            return false;
+        }
+
+        gid = strtoul(token, &endptr, 10);
+        if (*endptr != '\0') {
+            return false;
+        }
+
+        /*
+         * if unsigned long is greater than size of gid_t,
+         * prevent a truncation based roll-over
+         */
+        if (gid > GID_MAX) {
+            CLOGE("A gid in field \"gid list\" greater than GID_MAX");
+            return false;
+        }
+
+        gid_list[cmp++] = gid;
+    }
+    return true;
+}
+
+extern bool packagelist_parse(pfn_on_package callback, void *userdata)
+{
+
+    FILE *fp;
+    char *cur;
+    char *next;
+    char *endptr;
+    unsigned long tmp;
+    ssize_t bytesread;
+
+    bool rc = false;
+    char *buf = NULL;
+    size_t buflen = 0;
+    unsigned long lineno = 1;
+    const char *errmsg = NULL;
+    struct pkg_info *pkg_info = NULL;
+
+    fp = fopen(PACKAGES_LIST_FILE, "re");
+    if (!fp) {
+        CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
+                strerror(errno));
+        return false;
+    }
+
+    while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
+
+        pkg_info = calloc(1, sizeof(*pkg_info));
+        if (!pkg_info) {
+            goto err;
+        }
+
+        next = buf;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for \"package name\"";
+            goto err;
+        }
+
+        pkg_info->name = strdup(cur);
+        if (!pkg_info->name) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"uid\"";
+            goto err;
+        }
+
+        tmp = strtoul(cur, &endptr, 10);
+        if (*endptr != '\0') {
+            errmsg = "Could not convert field \"uid\" to integer value";
+            goto err;
+        }
+
+        /*
+         * if unsigned long is greater than size of uid_t,
+         * prevent a truncation based roll-over
+         */
+        if (tmp > UID_MAX) {
+            errmsg = "Field \"uid\" greater than UID_MAX";
+            goto err;
+        }
+
+        pkg_info->uid = (uid_t) tmp;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"debuggable\"";
+            goto err;
+        }
+
+        tmp = strtoul(cur, &endptr, 10);
+        if (*endptr != '\0') {
+            errmsg = "Could not convert field \"debuggable\" to integer value";
+            goto err;
+        }
+
+        /* should be a valid boolean of 1 or 0 */
+        if (!(tmp == 0 || tmp == 1)) {
+            errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
+            goto err;
+        }
+
+        pkg_info->debuggable = (bool) tmp;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"data dir\"";
+            goto err;
+        }
+
+        pkg_info->data_dir = strdup(cur);
+        if (!pkg_info->data_dir) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"seinfo\"";
+            goto err;
+        }
+
+        pkg_info->seinfo = strdup(cur);
+        if (!pkg_info->seinfo) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"gid(s)\"";
+            goto err;
+        }
+
+        /*
+         * Parse the gid list, could be in the form of none, single gid or list:
+         * none
+         * gid
+         * gid, gid ...
+         */
+        pkg_info->gids.cnt = get_gid_cnt(cur);
+        if (pkg_info->gids.cnt > 0) {
+
+            pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
+            if (!pkg_info->gids.gids) {
+                goto err;
+            }
+
+            rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
+            if (!rc) {
+                errmsg = "Could not parse field \"gid list\"";
+                goto err;
+            }
+        }
+
+        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) {
+            /*
+             * We do not log this as this can be intentional from
+             * callback to abort processing. We go to out to not
+             * free the pkg_info
+             */
+            rc = true;
+            goto out;
+        }
+        lineno++;
+    }
+
+    rc = true;
+
+out:
+    free(buf);
+    fclose(fp);
+    return rc;
+
+err:
+    if (errmsg) {
+        CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
+                PACKAGES_LIST_FILE, lineno, errmsg);
+    }
+    rc = false;
+    packagelist_free(pkg_info);
+    goto out;
+}
+
+void packagelist_free(pkg_info *info)
+{
+    if (info) {
+        free(info->name);
+        free(info->data_dir);
+        free(info->seinfo);
+        free(info->gids.gids);
+        free(info);
+    }
+}
diff --git a/libpackagelistparser/packagelistparser.cpp b/libpackagelistparser/packagelistparser.cpp
deleted file mode 100644
index 59c3a74..0000000
--- a/libpackagelistparser/packagelistparser.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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 "packagelistparser"
-
-#include <packagelistparser/packagelistparser.h>
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/limits.h>
-
-#include <memory>
-
-#include <log/log.h>
-
-static bool parse_gids(const char* path, size_t line_number, const char* gids, pkg_info* info) {
-  // Nothing to do?
-  if (!gids || !strcmp(gids, "none")) return true;
-
-  // How much space do we need?
-  info->gids.cnt = 1;
-  for (const char* p = gids; *p; ++p) {
-    if (*p == ',') ++info->gids.cnt;
-  }
-
-  // Allocate the space.
-  info->gids.gids = new gid_t[info->gids.cnt];
-  if (!info->gids.gids) return false;
-
-  // And parse the individual gids.
-  size_t i = 0;
-  while (true) {
-    char* end;
-    unsigned long gid = strtoul(gids, &end, 10);
-    if (gid > GID_MAX) {
-      ALOGE("%s:%zu: gid %lu > GID_MAX", path, line_number, gid);
-      return false;
-    }
-
-    if (i >= info->gids.cnt) return false;
-    info->gids.gids[i++] = gid;
-
-    if (*end == '\0') return true;
-    if (*end != ',') return false;
-    gids = end + 1;
-  }
-  return true;
-}
-
-static bool parse_line(const char* path, size_t line_number, const char* line, pkg_info* info) {
-  unsigned long uid;
-  int debuggable;
-  char* gid_list;
-  int profileable_from_shell = 0;
-
-  int fields =
-      sscanf(line, "%ms %lu %d %ms %ms %ms %d %ld", &info->name, &uid, &debuggable, &info->data_dir,
-             &info->seinfo, &gid_list, &profileable_from_shell, &info->version_code);
-
-  // Handle the more complicated gids field and free the temporary string.
-  bool gids_okay = parse_gids(path, line_number, gid_list, info);
-  free(gid_list);
-  if (!gids_okay) return false;
-
-  // Did we see enough fields to be getting on with?
-  // The final fields are optional (and not usually present).
-  if (fields < 6) {
-    ALOGE("%s:%zu: too few fields in line", path, line_number);
-    return false;
-  }
-
-  // Extra validation.
-  if (uid > UID_MAX) {
-    ALOGE("%s:%zu: uid %lu > UID_MAX", path, line_number, uid);
-    return false;
-  }
-  info->uid = uid;
-
-  // Integer to bool conversions.
-  info->debuggable = debuggable;
-  info->profileable_from_shell = profileable_from_shell;
-
-  return true;
-}
-
-bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info*, void*), void* user_data) {
-  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path, "re"), &fclose);
-  if (!fp) {
-    ALOGE("couldn't open '%s': %s", path, strerror(errno));
-    return false;
-  }
-
-  size_t line_number = 0;
-  char* line = nullptr;
-  size_t allocated_length = 0;
-  while (getline(&line, &allocated_length, fp.get()) > 0) {
-    ++line_number;
-    std::unique_ptr<pkg_info, decltype(&packagelist_free)> info(
-        static_cast<pkg_info*>(calloc(1, sizeof(pkg_info))), &packagelist_free);
-    if (!info) {
-      ALOGE("%s:%zu: couldn't allocate pkg_info", path, line_number);
-      return false;
-    }
-
-    if (!parse_line(path, line_number, line, info.get())) return false;
-
-    if (!callback(info.release(), user_data)) break;
-  }
-  free(line);
-  return true;
-}
-
-bool packagelist_parse(bool (*callback)(pkg_info*, void*), void* user_data) {
-  return packagelist_parse_file("/data/system/packages.list", callback, user_data);
-}
-
-void packagelist_free(pkg_info* info) {
-  if (!info) return;
-
-  free(info->name);
-  free(info->data_dir);
-  free(info->seinfo);
-  delete[] info->gids.gids;
-  free(info);
-}
diff --git a/libpackagelistparser/packagelistparser_test.cpp b/libpackagelistparser/packagelistparser_test.cpp
deleted file mode 100644
index 76cb886..0000000
--- a/libpackagelistparser/packagelistparser_test.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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 <packagelistparser/packagelistparser.h>
-
-#include <memory>
-
-#include <android-base/file.h>
-
-#include <gtest/gtest.h>
-
-TEST(packagelistparser, smoke) {
-  TemporaryFile tf;
-  android::base::WriteStringToFile(
-      // No gids.
-      "com.test.a0 10014 0 /data/user/0/com.test.a0 platform:privapp:targetSdkVersion=19 none\n"
-      // One gid.
-      "com.test.a1 10007 1 /data/user/0/com.test.a1 platform:privapp:targetSdkVersion=21 1023\n"
-      // Multiple gids.
-      "com.test.a2 10011 0 /data/user/0/com.test.a2 media:privapp:targetSdkVersion=30 "
-      "2001,1065,1023,3003,3007,1024\n"
-      // The two new fields (profileable flag and version code).
-      "com.test.a3 10022 0 /data/user/0/com.test.a3 selabel:blah none 1 123\n",
-      tf.path);
-
-  std::vector<pkg_info*> packages;
-  packagelist_parse_file(
-      tf.path,
-      [](pkg_info* info, void* user_data) -> bool {
-        reinterpret_cast<std::vector<pkg_info*>*>(user_data)->push_back(info);
-        return true;
-      },
-      &packages);
-
-  ASSERT_EQ(4U, packages.size());
-
-  ASSERT_STREQ("com.test.a0", packages[0]->name);
-  ASSERT_EQ(10014, packages[0]->uid);
-  ASSERT_FALSE(packages[0]->debuggable);
-  ASSERT_STREQ("/data/user/0/com.test.a0", packages[0]->data_dir);
-  ASSERT_STREQ("platform:privapp:targetSdkVersion=19", packages[0]->seinfo);
-  ASSERT_EQ(0U, packages[0]->gids.cnt);
-  ASSERT_FALSE(packages[0]->profileable_from_shell);
-  ASSERT_EQ(0, packages[0]->version_code);
-
-  ASSERT_STREQ("com.test.a1", packages[1]->name);
-  ASSERT_EQ(10007, packages[1]->uid);
-  ASSERT_TRUE(packages[1]->debuggable);
-  ASSERT_STREQ("/data/user/0/com.test.a1", packages[1]->data_dir);
-  ASSERT_STREQ("platform:privapp:targetSdkVersion=21", packages[1]->seinfo);
-  ASSERT_EQ(1U, packages[1]->gids.cnt);
-  ASSERT_EQ(1023U, packages[1]->gids.gids[0]);
-  ASSERT_FALSE(packages[0]->profileable_from_shell);
-  ASSERT_EQ(0, packages[0]->version_code);
-
-  ASSERT_STREQ("com.test.a2", packages[2]->name);
-  ASSERT_EQ(10011, packages[2]->uid);
-  ASSERT_FALSE(packages[2]->debuggable);
-  ASSERT_STREQ("/data/user/0/com.test.a2", packages[2]->data_dir);
-  ASSERT_STREQ("media:privapp:targetSdkVersion=30", packages[2]->seinfo);
-  ASSERT_EQ(6U, packages[2]->gids.cnt);
-  ASSERT_EQ(2001U, packages[2]->gids.gids[0]);
-  ASSERT_EQ(1024U, packages[2]->gids.gids[5]);
-  ASSERT_FALSE(packages[0]->profileable_from_shell);
-  ASSERT_EQ(0, packages[0]->version_code);
-
-  ASSERT_STREQ("com.test.a3", packages[3]->name);
-  ASSERT_EQ(10022, packages[3]->uid);
-  ASSERT_FALSE(packages[3]->debuggable);
-  ASSERT_STREQ("/data/user/0/com.test.a3", packages[3]->data_dir);
-  ASSERT_STREQ("selabel:blah", packages[3]->seinfo);
-  ASSERT_EQ(0U, packages[3]->gids.cnt);
-  ASSERT_TRUE(packages[3]->profileable_from_shell);
-  ASSERT_EQ(123, packages[3]->version_code);
-
-  for (auto& package : packages) packagelist_free(package);
-}
-
-TEST(packagelistparser, early_exit) {
-  TemporaryFile tf;
-  android::base::WriteStringToFile(
-      "com.test.a0 1 0 / a none\n"
-      "com.test.a1 1 0 / a none\n"
-      "com.test.a2 1 0 / a none\n",
-      tf.path);
-
-  std::vector<pkg_info*> packages;
-  packagelist_parse_file(
-      tf.path,
-      [](pkg_info* info, void* user_data) -> bool {
-        std::vector<pkg_info*>* p = reinterpret_cast<std::vector<pkg_info*>*>(user_data);
-        p->push_back(info);
-        return p->size() < 2;
-      },
-      &packages);
-
-  ASSERT_EQ(2U, packages.size());
-
-  ASSERT_STREQ("com.test.a0", packages[0]->name);
-  ASSERT_STREQ("com.test.a1", packages[1]->name);
-
-  for (auto& package : packages) packagelist_free(package);
-}
-
-TEST(packagelistparser, system_package_list) {
-  // Check that we can actually read the packages.list installed on the device.
-  std::vector<pkg_info*> packages;
-  packagelist_parse(
-      [](pkg_info* info, void* user_data) -> bool {
-        reinterpret_cast<std::vector<pkg_info*>*>(user_data)->push_back(info);
-        return true;
-      },
-      &packages);
-  // Not much we can say for sure about what we expect, other than that there
-  // are likely to be lots of packages...
-  ASSERT_GT(packages.size(), 10U);
-}
-
-TEST(packagelistparser, packagelist_free_nullptr) {
-  packagelist_free(nullptr);
-}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 618a5c5..0207a75 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -3,7 +3,6 @@
     vendor_available: true,
     recovery_available: true,
     host_supported: true,
-    native_bridge_supported: true,
     export_include_dirs: ["include"],
     target: {
         linux_bionic: {
@@ -24,7 +23,6 @@
     ],
     name: "libprocessgroup",
     host_supported: true,
-    native_bridge_supported: true,
     recovery_available: true,
     vendor_available: true,
     vndk: {
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
index 27b9a03..bfa730a 100644
--- a/libprocessgroup/OWNERS
+++ b/libprocessgroup/OWNERS
@@ -1,3 +1,2 @@
 ccross@google.com
-surenb@google.com
 tomcherry@google.com
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
index 0af75bb..9d5afeb 100644
--- a/libprocessgroup/cgrouprc/Android.bp
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -21,7 +21,6 @@
     // 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",
@@ -56,7 +55,6 @@
 llndk_library {
     name: "libcgrouprc",
     symbol_file: "libcgrouprc.llndk.txt",
-    native_bridge_supported: true,
     export_include_dirs: [
         "include",
     ],
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
index 559a869..dfbeed7 100644
--- a/libprocessgroup/cgrouprc_format/Android.bp
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -16,7 +16,6 @@
     name: "libcgrouprc_format",
     host_supported: true,
     recovery_available: true,
-    native_bridge_supported: true,
     srcs: [
         "cgroup_controller.cpp",
     ],
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index f73ec2d..7e6bf45 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -39,11 +39,6 @@
 
 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.
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
index 945d90c..3c498da 100644
--- a/libprocessgroup/include/processgroup/sched_policy.h
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -70,22 +70,11 @@
 extern int get_sched_policy(int tid, SchedPolicy* policy);
 
 /* Return a displayable string corresponding to policy.
- * Return value: NUL-terminated name of unspecified length, nullptr if invalid;
+ * 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);
 
-/* Return the aggregated task profile name corresponding to cpuset policy.
- * Return value: NUL-terminated name of unspecified length, nullptr if invalid;
- * the caller could use it to call SetTaskProfiles.
- */
-extern const char* get_cpuset_policy_profile_name(SchedPolicy policy);
-
-/* Return the aggregated task profile name corresponding to sched policy.
- * Return value: NUL-terminated name of unspecified length, nullptr if invalid;
- * the caller could use it to call SetTaskProfiles.
- */
-extern const char* get_sched_policy_profile_name(SchedPolicy policy);
 #ifdef __cplusplus
 }
 #endif
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 7b6dde2..d3ac26b 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -111,17 +111,45 @@
     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) {
-    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, 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 {
+            PLOG(WARNING) << "Failed to find " << name << "process profile";
+        }
+    }
+
+    return true;
 }
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
-    return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, 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 ConvertUidToPath(const char* cgroup, uid_t uid) {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 608f007..74a39cd 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -15,6 +15,7 @@
       "Controller": "cpuset",
       "File": "top-app/cpus"
     },
+
     {
       "Name": "MemLimit",
       "Controller": "memory",
@@ -493,52 +494,5 @@
         }
       ]
     }
-  ],
-
-  "AggregateProfiles": [
-    {
-      "Name": "SCHED_SP_DEFAULT",
-      "Profiles": [ "TimerSlackNormal" ]
-    },
-    {
-      "Name": "SCHED_SP_BACKGROUND",
-      "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
-    },
-    {
-      "Name": "SCHED_SP_FOREGROUND",
-      "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
-    },
-    {
-      "Name": "SCHED_SP_TOP_APP",
-      "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
-    },
-    {
-      "Name": "SCHED_SP_RT_APP",
-      "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
-    },
-    {
-      "Name": "CPUSET_SP_DEFAULT",
-      "Profiles": [ "TimerSlackNormal" ]
-    },
-    {
-      "Name": "CPUSET_SP_BACKGROUND",
-      "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
-    },
-    {
-      "Name": "CPUSET_SP_FOREGROUND",
-      "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
-    },
-    {
-      "Name": "CPUSET_SP_TOP_APP",
-      "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
-    },
-    {
-      "Name": "CPUSET_SP_SYSTEM",
-      "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
-    },
-    {
-      "Name": "CPUSET_SP_RESTRICTED",
-      "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
-    }
   ]
 }
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
index 1de4395..578f0d3 100644
--- a/libprocessgroup/profiles/task_profiles.proto
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -18,11 +18,10 @@
 
 package android.profiles;
 
-// Next: 4
+// Next: 3
 message TaskProfiles {
     repeated Attribute attributes = 1 [json_name = "Attributes"];
     repeated Profile profiles = 2 [json_name = "Profiles"];
-    repeated AggregateProfiles aggregateprofiles = 3 [json_name = "AggregateProfiles"];
 }
 
 // Next: 4
@@ -43,9 +42,3 @@
     string name = 1 [json_name = "Name"];
     map<string, string> params = 2 [json_name = "Params"];
 }
-
-// Next: 3
-message AggregateProfiles {
-    string name = 1 [json_name = "Name"];
-    repeated string profiles = 2 [json_name = "Profiles"];
-}
diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp
index b37e3e6..bc9aade 100644
--- a/libprocessgroup/profiles/test.cpp
+++ b/libprocessgroup/profiles/test.cpp
@@ -33,14 +33,14 @@
 }
 
 // Test suite instantiations
-INSTANTIATE_TEST_SUITE_P(Cgroups, JsonSchemaTest,
+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(Cgroups, CgroupsTest,
+INSTANTIATE_TEST_SUITE_P(, CgroupsTest,
                          ::testing::Values(MakeTestParam<Cgroups>("/cgroups.json"),
                                            MakeTestParam<Cgroups>("/cgroups.recovery.json")));
-INSTANTIATE_TEST_SUITE_P(TaskProfiles, TaskProfilesTest,
+INSTANTIATE_TEST_SUITE_P(, TaskProfilesTest,
                          ::testing::Values(MakeTestParam<TaskProfiles>("/task_profiles.json")));
 
 }  // namespace profiles
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 16339d3..15f8139 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -46,17 +46,34 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid, {"CPUSET_SP_BACKGROUND"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid,
+                                   {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
+                                    "TimerSlackHigh"},
+                                   true)
+                           ? 0
+                           : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid,
+                                   {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
+                                    "TimerSlackNormal"},
+                                   true)
+                           ? 0
+                           : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid, {"CPUSET_SP_TOP_APP"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid,
+                                   {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
+                                    "TimerSlackNormal"},
+                                   true)
+                           ? 0
+                           : -1;
         case SP_SYSTEM:
-            return SetTaskProfiles(tid, {"CPUSET_SP_SYSTEM"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}, true) ? 0 : -1;
         case SP_RESTRICTED:
-            return SetTaskProfiles(tid, {"CPUSET_SP_RESTRICTED"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}, true)
+                           ? 0
+                           : -1;
         default:
             break;
     }
@@ -117,17 +134,17 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"HighEnergySaving", "TimerSlackHigh"}, true) ? 0 : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"HighPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"MaxPerformance", "TimerSlackNormal"}, true) ? 0 : -1;
         case SP_RT_APP:
-            return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"RealtimePerformance", "TimerSlackNormal"}, true) ? 0 : -1;
         default:
-            return SetTaskProfiles(tid, {"SCHED_SP_DEFAULT"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
     }
 
     return 0;
@@ -212,45 +229,7 @@
     };
     static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
     if (policy < SP_BACKGROUND || policy >= SP_CNT) {
-        return nullptr;
+        return "error";
     }
     return kSchedPolicyNames[policy];
 }
-
-const char* get_cpuset_policy_profile_name(SchedPolicy policy) {
-    /*
-     *  cpuset profile array for:
-     *  SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
-     *  SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
-     *  SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7)
-     *  index is policy + 1
-     *  this need keep in sync with SchedPolicy enum
-     */
-    static constexpr const char* kCpusetProfiles[SP_CNT + 1] = {
-            "CPUSET_SP_DEFAULT", "CPUSET_SP_BACKGROUND", "CPUSET_SP_FOREGROUND",
-            "CPUSET_SP_SYSTEM",  "CPUSET_SP_FOREGROUND", "CPUSET_SP_FOREGROUND",
-            "CPUSET_SP_TOP_APP", "CPUSET_SP_DEFAULT",    "CPUSET_SP_RESTRICTED"};
-    if (policy < SP_DEFAULT || policy >= SP_CNT) {
-        return nullptr;
-    }
-    return kCpusetProfiles[policy + 1];
-}
-
-const char* get_sched_policy_profile_name(SchedPolicy policy) {
-    /*
-     *  sched profile array for:
-     *  SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
-     *  SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
-     *  SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7)
-     *  index is policy + 1
-     *  this need keep in sync with SchedPolicy enum
-     */
-    static constexpr const char* kSchedProfiles[SP_CNT + 1] = {
-            "SCHED_SP_DEFAULT", "SCHED_SP_BACKGROUND", "SCHED_SP_FOREGROUND",
-            "SCHED_SP_DEFAULT", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
-            "SCHED_SP_TOP_APP", "SCHED_SP_RT_APP",     "SCHED_SP_DEFAULT"};
-    if (policy < SP_DEFAULT || policy >= SP_CNT) {
-        return nullptr;
-    }
-    return kSchedProfiles[policy + 1];
-}
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 9447f86..edc316a 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -173,15 +173,6 @@
     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;
@@ -268,26 +259,6 @@
     return true;
 }
 
-bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
-    for (const auto& profile : profiles_) {
-        profile->EnableResourceCaching();
-        if (!profile->ExecuteForProcess(uid, pid)) {
-            PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
-        }
-    }
-    return true;
-}
-
-bool ApplyProfileAction::ExecuteForTask(int tid) const {
-    for (const auto& profile : profiles_) {
-        profile->EnableResourceCaching();
-        if (!profile->ExecuteForTask(tid)) {
-            PLOG(WARNING) << "ExecuteForTask failed for aggregate profile";
-        }
-    }
-    return true;
-}
-
 bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& element : elements_) {
         if (!element->ExecuteForProcess(uid, pid)) {
@@ -321,24 +292,6 @@
     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.
@@ -393,13 +346,15 @@
         }
     }
 
+    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_shared<TaskProfile>();
+        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];
@@ -458,38 +413,7 @@
                 LOG(WARNING) << "Unknown profile action: " << action_name;
             }
         }
-        profiles_[profile_name] = profile;
-    }
-
-    const Json::Value& aggregateprofiles_val = root["AggregateProfiles"];
-    for (Json::Value::ArrayIndex i = 0; i < aggregateprofiles_val.size(); ++i) {
-        const Json::Value& aggregateprofile_val = aggregateprofiles_val[i];
-
-        std::string aggregateprofile_name = aggregateprofile_val["Name"].asString();
-        const Json::Value& aggregateprofiles = aggregateprofile_val["Profiles"];
-        std::vector<std::shared_ptr<TaskProfile>> profiles;
-        bool ret = true;
-
-        for (Json::Value::ArrayIndex pf_idx = 0; pf_idx < aggregateprofiles.size(); ++pf_idx) {
-            std::string profile_name = aggregateprofiles[pf_idx].asString();
-
-            if (profile_name == aggregateprofile_name) {
-                LOG(WARNING) << "AggregateProfiles: recursive profile name: " << profile_name;
-                ret = false;
-                break;
-            } else if (profiles_.find(profile_name) == profiles_.end()) {
-                LOG(WARNING) << "AggregateProfiles: undefined profile name: " << profile_name;
-                ret = false;
-                break;
-            } else {
-                profiles.push_back(profiles_[profile_name]);
-            }
-        }
-        if (ret) {
-            auto profile = std::make_shared<TaskProfile>();
-            profile->Add(std::make_unique<ApplyProfileAction>(profiles));
-            profiles_[aggregateprofile_name] = profile;
-        }
+        profiles_[profile_name] = std::move(profile);
     }
 
     return true;
@@ -512,39 +436,3 @@
     }
     return nullptr;
 }
-
-bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
-                                      const std::vector<std::string>& profiles, bool use_fd_cache) {
-    for (const auto& name : profiles) {
-        TaskProfile* profile = 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 {
-            PLOG(WARNING) << "Failed to find " << name << "process profile";
-        }
-    }
-    return true;
-}
-
-bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
-                                   bool use_fd_cache) {
-    for (const auto& name : profiles) {
-        TaskProfile* profile = 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;
-}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 9f2308c..77bac2d 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -51,7 +51,6 @@
     virtual bool ExecuteForTask(int) const { return false; };
 
     virtual void EnableResourceCaching() {}
-    virtual void DropResourceCaching() {}
 };
 
 // Profile actions
@@ -115,7 +114,6 @@
     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_; }
@@ -147,26 +145,12 @@
     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_;
 };
 
-// Set aggregate profile element
-class ApplyProfileAction : public ProfileAction {
-  public:
-    ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
-        : profiles_(profiles) {}
-
-    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
-    virtual bool ExecuteForTask(int tid) const;
-
-  private:
-    std::vector<std::shared_ptr<TaskProfile>> profiles_;
-};
-
 class TaskProfiles {
   public:
     // Should be used by all users
@@ -174,13 +158,9 @@
 
     TaskProfile* GetProfile(const std::string& name) const;
     const ProfileAttribute* GetAttribute(const std::string& name) const;
-    void DropResourceCaching() const;
-    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
-                            bool use_fd_cache);
-    bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
 
   private:
-    std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
+    std::map<std::string, std::unique_ptr<TaskProfile>> profiles_;
     std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
 
     TaskProfiles();
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 0c9a2b8..15f03d0 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -34,7 +34,6 @@
     host_supported: true,
     srcs: [
         "process.cpp",
-        "process_map.cpp",
     ],
 
     local_include_dirs: ["include"],
@@ -59,7 +58,6 @@
     name: "libprocinfo_test",
     defaults: ["libprocinfo_defaults"],
     host_supported: true,
-    isolated: true,
     srcs: [
         "process_test.cpp",
         "process_map_test.cpp",
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index 569a022..b6ec3cb 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -176,9 +176,5 @@
                const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
 }
 
-bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
-                          const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
-                                                   const char*)>& callback);
-
 } /* namespace procinfo */
 } /* namespace android */
diff --git a/libprocinfo/process_map.cpp b/libprocinfo/process_map.cpp
deleted file mode 100644
index 5e240b9..0000000
--- a/libprocinfo/process_map.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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 <procinfo/process_map.h>
-
-#include <fcntl.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <procinfo/process.h>
-
-namespace android {
-namespace procinfo {
-
-bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
-                          const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
-                                                   const char*)>& callback) {
-  if (buffer == nullptr || buffer_size == 0) {
-    return false;
-  }
-
-  int fd = open(map_file, O_RDONLY | O_CLOEXEC);
-  if (fd == -1) {
-    return false;
-  }
-
-  char* char_buffer = reinterpret_cast<char*>(buffer);
-  size_t start = 0;
-  size_t read_bytes = 0;
-  char* line = nullptr;
-  bool read_complete = false;
-  while (true) {
-    ssize_t bytes =
-        TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
-    if (bytes <= 0) {
-      if (read_bytes == 0) {
-        close(fd);
-        return bytes == 0;
-      }
-      // Treat the last piece of data as the last line.
-      char_buffer[start + read_bytes] = '\n';
-      bytes = 1;
-      read_complete = true;
-    }
-    read_bytes += bytes;
-
-    while (read_bytes > 0) {
-      char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
-      if (newline == nullptr) {
-        break;
-      }
-      *newline = '\0';
-      line = &char_buffer[start];
-      start = newline - char_buffer + 1;
-      read_bytes -= newline - line + 1;
-
-      // Ignore the return code, errors are okay.
-      ReadMapFileContent(line, callback);
-    }
-
-    if (read_complete) {
-      close(fd);
-      return true;
-    }
-
-    if (start == 0 && read_bytes == buffer_size - 1) {
-      // The buffer provided is too small to contain this line, give up
-      // and indicate failure.
-      close(fd);
-      return false;
-    }
-
-    // Copy any leftover data to the front  of the buffer.
-    if (start > 0) {
-      if (read_bytes > 0) {
-        memmove(char_buffer, &char_buffer[start], read_bytes);
-      }
-      start = 0;
-    }
-  }
-}
-
-} /* namespace procinfo */
-} /* namespace android */
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index b1bdc08..562d864 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -16,14 +16,9 @@
 
 #include <procinfo/process_map.h>
 
-#include <inttypes.h>
-#include <sys/mman.h>
-
 #include <string>
-#include <vector>
 
 #include <android-base/file.h>
-#include <android-base/stringprintf.h>
 
 #include <gtest/gtest.h>
 
@@ -68,215 +63,3 @@
   ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
   ASSERT_GT(maps.size(), 0u);
 }
-
-extern "C" void malloc_disable();
-extern "C" void malloc_enable();
-
-struct TestMapInfo {
-  TestMapInfo() = default;
-  TestMapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
-              const char* new_name)
-      : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode) {
-    strcpy(name, new_name);
-  }
-  uint64_t start = 0;
-  uint64_t end = 0;
-  uint16_t flags = 0;
-  uint64_t pgoff = 0;
-  ino_t inode = 0;
-  char name[100] = {};
-};
-
-void VerifyReadMapFileAsyncSafe(const char* maps_data,
-                                const std::vector<TestMapInfo>& expected_info) {
-  TemporaryFile tf;
-  ASSERT_TRUE(android::base::WriteStringToFd(maps_data, tf.fd));
-
-  std::vector<TestMapInfo> saved_info(expected_info.size());
-  size_t num_maps = 0;
-
-  auto callback = [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
-                      const char* name) {
-    if (num_maps != saved_info.size()) {
-      TestMapInfo& saved = saved_info[num_maps];
-      saved.start = start;
-      saved.end = end;
-      saved.flags = flags;
-      saved.pgoff = pgoff;
-      saved.inode = inode;
-      strcpy(saved.name, name);
-    }
-    num_maps++;
-  };
-
-  std::vector<char> buffer(64 * 1024);
-
-#if defined(__BIONIC__)
-  // Any allocations will block after this call.
-  malloc_disable();
-#endif
-
-  bool parsed =
-      android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer.data(), buffer.size(), callback);
-
-#if defined(__BIONIC__)
-  malloc_enable();
-#endif
-
-  ASSERT_TRUE(parsed) << "Parsing of data failed:\n" << maps_data;
-  ASSERT_EQ(expected_info.size(), num_maps);
-  for (size_t i = 0; i < expected_info.size(); i++) {
-    const TestMapInfo& expected = expected_info[i];
-    const TestMapInfo& saved = saved_info[i];
-    EXPECT_EQ(expected.start, saved.start);
-    EXPECT_EQ(expected.end, saved.end);
-    EXPECT_EQ(expected.flags, saved.flags);
-    EXPECT_EQ(expected.pgoff, saved.pgoff);
-    EXPECT_EQ(expected.inode, saved.inode);
-    EXPECT_STREQ(expected.name, saved.name);
-  }
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_invalid) {
-  std::vector<TestMapInfo> expected_info;
-
-  VerifyReadMapFileAsyncSafe("12c00000-2ac00000", expected_info);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_single) {
-  std::vector<TestMapInfo> expected_info;
-  expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
-                             "/lib/fake.so");
-
-  VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so",
-                             expected_info);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_single_with_newline) {
-  std::vector<TestMapInfo> expected_info;
-  expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
-                             "/lib/fake.so");
-
-  VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so\n",
-                             expected_info);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_single_no_library) {
-  std::vector<TestMapInfo> expected_info;
-  expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 0xb00, 101, "");
-
-  VerifyReadMapFileAsyncSafe("a0000-c0000 rwxp 00000b00 00:05 101", expected_info);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_multiple) {
-  std::vector<TestMapInfo> expected_info;
-  expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 1, 100, "");
-  expected_info.emplace_back(0xd0000, 0xe0000, PROT_READ, 2, 101, "/lib/libsomething1.so");
-  expected_info.emplace_back(0xf0000, 0x100000, PROT_WRITE, 3, 102, "/lib/libsomething2.so");
-  expected_info.emplace_back(0x110000, 0x120000, PROT_EXEC, 4, 103, "[anon:something or another]");
-
-  std::string map_data =
-      "0a0000-0c0000 rwxp 00000001 00:05 100\n"
-      "0d0000-0e0000 r--p 00000002 00:05 101  /lib/libsomething1.so\n"
-      "0f0000-100000 -w-p 00000003 00:05 102  /lib/libsomething2.so\n"
-      "110000-120000 --xp 00000004 00:05 103  [anon:something or another]\n";
-
-  VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_multiple_reads) {
-  std::vector<TestMapInfo> expected_info;
-  std::string map_data;
-  uint64_t start = 0xa0000;
-  for (size_t i = 0; i < 10000; i++) {
-    map_data += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r--p %zx 01:20 %zu fake.so\n",
-                                            start, start + 0x1000, i, 1000 + i);
-    expected_info.emplace_back(start, start + 0x1000, PROT_READ, i, 1000 + i, "fake.so");
-  }
-
-  VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_buffer_nullptr) {
-  size_t num_calls = 0;
-  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
-
-#if defined(__BIONIC__)
-  // Any allocations will block after this call.
-  malloc_disable();
-#endif
-
-  bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", nullptr, 10, callback);
-
-#if defined(__BIONIC__)
-  malloc_enable();
-#endif
-
-  ASSERT_FALSE(parsed);
-  EXPECT_EQ(0UL, num_calls);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_buffer_size_zero) {
-  size_t num_calls = 0;
-  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
-
-#if defined(__BIONIC__)
-  // Any allocations will block after this call.
-  malloc_disable();
-#endif
-
-  char buffer[10];
-  bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, 0, callback);
-
-#if defined(__BIONIC__)
-  malloc_enable();
-#endif
-
-  ASSERT_FALSE(parsed);
-  EXPECT_EQ(0UL, num_calls);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_no_calls) {
-  size_t num_calls = 0;
-  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
-
-#if defined(__BIONIC__)
-  // Any allocations will block after this call.
-  malloc_disable();
-#endif
-
-  char buffer[10];
-  bool parsed =
-      android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, sizeof(buffer), callback);
-
-#if defined(__BIONIC__)
-  malloc_enable();
-#endif
-
-  ASSERT_FALSE(parsed);
-  EXPECT_EQ(0UL, num_calls);
-}
-
-TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_could_parse) {
-  TemporaryFile tf;
-  ASSERT_TRUE(android::base::WriteStringToFd(
-      "0a0000-0c0000 rwxp 00000001 00:05 100    /fake/lib.so\n", tf.fd));
-
-  size_t num_calls = 0;
-  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
-
-#if defined(__BIONIC__)
-  // Any allocations will block after this call.
-  malloc_disable();
-#endif
-
-  char buffer[39];
-  bool parsed = android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer, sizeof(buffer), callback);
-
-#if defined(__BIONIC__)
-  malloc_enable();
-#endif
-
-  ASSERT_FALSE(parsed);
-  EXPECT_EQ(0UL, num_calls);
-}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 88146e9..2ec4754 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -82,15 +82,3 @@
         },
     },
 }
-
-cc_fuzz {
-    name: "sparse_fuzzer",
-    host_supported: false,
-    srcs: [
-        "sparse_fuzzer.cpp",
-    ],
-    static_libs: [
-        "libsparse",
-        "liblog",
-    ],
-}
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index c5c4960..5b8179f 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -17,7 +17,6 @@
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
-#include <algorithm>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <limits.h>
@@ -49,6 +48,13 @@
 #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))
@@ -225,7 +231,7 @@
   struct output_file_gz* outgz = to_output_file_gz(out);
 
   while (len > 0) {
-    ret = gzwrite(outgz->gz_fd, data, std::min<unsigned int>(len, (unsigned int)INT_MAX));
+    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;
@@ -262,7 +268,7 @@
   int ret;
 
   while (off > 0) {
-    to_write = std::min(off, (int64_t)INT_MAX);
+    to_write = min(off, (int64_t)INT_MAX);
     ret = outc->write(outc->priv, nullptr, to_write);
     if (ret < 0) {
       return ret;
@@ -464,7 +470,7 @@
   }
 
   while (len) {
-    write_len = std::min(len, out->block_size);
+    write_len = min(len, out->block_size);
     ret = out->ops->write(out, out->fill_buf, write_len);
     if (ret < 0) {
       return ret;
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index 24c6379..cb288c5 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -188,7 +188,7 @@
                               int (*write)(void* priv, const void* data, size_t len,
                                            unsigned int block, unsigned int nr_blocks),
                               void* priv) {
-  int ret = 0;
+  int ret;
   int chunks;
   struct chunk_data chk;
   struct output_file* out;
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
deleted file mode 100644
index 42f331f..0000000
--- a/libsparse/sparse_fuzzer.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "include/sparse/sparse.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size < 2 * sizeof(wchar_t)) return 0;
-
-  int64_t blocksize = 4096;
-  struct sparse_file* file = sparse_file_new(size, blocksize);
-  if (!file) {
-    return 0;
-  }
-
-  unsigned int block = 1;
-  sparse_file_add_data(file, &data, size, block);
-  sparse_file_destroy(file);
-  return 0;
-}
diff --git a/libstats/OWNERS b/libstats/OWNERS
index 7855774..ed06fbc 100644
--- a/libstats/OWNERS
+++ b/libstats/OWNERS
@@ -1,7 +1,4 @@
+bookatz@google.com
 joeo@google.com
-muhammadq@google.com
-ruchirr@google.com
-singhtejinder@google.com
-tsaichristine@google.com
 yaochen@google.com
-yro@google.com
+yanglu@google.com
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index b7ada0c..845a197 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef ANDROID_STATS_LOG_STATS_EVENT_LIST_H
+#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
 
 #include <log/log_event_list.h>
 #include <sys/uio.h>
@@ -132,6 +133,7 @@
         return *this;
     }
 
+#if defined(_USING_LIBCXX)
     stats_event_list& operator<<(const std::string& value) {
         int retval = android_log_write_string8_len(ctx, value.data(), value.length());
         if (retval < 0) {
@@ -139,6 +141,7 @@
         }
         return *this;
     }
+#endif
 
     stats_event_list& operator<<(float value) {
         int retval = android_log_write_float32(ctx, value);
@@ -200,6 +203,7 @@
         return ret >= 0;
     }
 
+#if defined(_USING_LIBCXX)
     bool AppendString(const std::string& value) {
         int retval = android_log_write_string8_len(ctx, value.data(), value.length());
         if (retval < 0) {
@@ -215,6 +219,7 @@
         }
         return ret;
     }
+#endif
 
     bool AppendFloat(float value) {
         int retval = android_log_write_float32(ctx, value);
@@ -248,3 +253,4 @@
 };
 
 #endif
+#endif  // ANDROID_STATS_LOG_STATS_EVENT_LIST_H
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index 073b67f..b1c05ea 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -36,6 +36,25 @@
 #include <time.h>
 #include <unistd.h>
 
+/* 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;
@@ -202,14 +221,14 @@
             android_log_event_long_t buffer;
             header.id = LOG_ID_STATS;
             // store the last log error in the tag field. This tag field is not used by statsd.
-            buffer.header.tag = atomic_load(&log_error);
+            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 = composed_long;
+            buffer.payload.data = htole64(composed_long);
 
             newVec[headerLength].iov_base = &buffer;
             newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/libsync/Android.bp b/libsync/Android.bp
index c996e1b..e56f8ba 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -23,7 +23,6 @@
 cc_library {
     name: "libsync",
     recovery_available: true,
-    native_bridge_supported: true,
     defaults: ["libsync_defaults"],
 }
 
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index b265b61..2e22b43 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -3,7 +3,6 @@
     vendor_available: true,
     recovery_available: true,
     host_supported: true,
-    native_bridge_supported: true,
     export_include_dirs: ["include"],
 
     target: {
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index ccda5d1..da5d86c 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -26,19 +26,6 @@
     ],
 
     export_include_dirs: ["include"],
-
-    tidy: true,
-    tidy_checks: [
-        "-*",
-        "cert-*",
-        "clang-analyzer-security*",
-        "android-*",
-    ],
-    tidy_checks_as_errors: [
-        "cert-*",
-        "clang-analyzer-security*",
-        "android-*",
-    ],
 }
 
 cc_test {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 8fe7854..9dc2699 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -39,12 +39,9 @@
 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));
@@ -304,9 +301,8 @@
         raw = (char*)nlAttrData(payload);
     }
 
-    size_t hexSize = 5 + (len * 2);
-    char* hex = (char*)calloc(1, hexSize);
-    strlcpy(hex, "HEX=", hexSize);
+    char* hex = (char*) calloc(1, 5 + (len * 2));
+    strcpy(hex, "HEX=");
     for (int i = 0; i < len; i++) {
         hex[4 + (i * 2)] = "0123456789abcdef"[(raw[i] >> 4) & 0xf];
         hex[5 + (i * 2)] = "0123456789abcdef"[raw[i] & 0xf];
@@ -478,20 +474,23 @@
         struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
         const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
 
-        // Construct a comma-separated string of DNS addresses.
+        // Construct "SERVERS=<comma-separated string of DNS addresses>".
+        static const char kServerTag[] = "SERVERS=";
+        static const size_t kTagLength = strlen(kServerTag);
         // 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 = numaddrs * kMaxSingleAddressLength;
+        const size_t bufsize = kTagLength + 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++] = ',';
@@ -509,8 +508,7 @@
         mSubsystem = strdup("net");
         asprintf(&mParams[0], "INTERFACE=%s", ifname);
         asprintf(&mParams[1], "LIFETIME=%u", lifetime);
-        asprintf(&mParams[2], "SERVERS=%s", buf);
-        free(buf);
+        mParams[2] = buf;
     } else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {
         // TODO: support DNSSL.
     } else {
@@ -636,9 +634,7 @@
                 else if (!strcmp(a, "change"))
                     mAction = Action::kChange;
             } else if ((a = HAS_CONST_PREFIX(s, end, "SEQNUM=")) != nullptr) {
-                if (!ParseInt(a, &mSeq)) {
-                    SLOGE("NetlinkEvent::parseAsciiNetlinkMessage: failed to parse SEQNUM=%s", a);
-                }
+                mSeq = atoi(a);
             } else if ((a = HAS_CONST_PREFIX(s, end, "SUBSYSTEM=")) != nullptr) {
                 mSubsystem = strdup(a);
             } else if (param_idx < NL_PARAMS_MAX) {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 2573b1c..b7650a1 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -125,6 +125,10 @@
         },
     },
 
+    whole_static_libs: [
+        "libdemangle"
+    ],
+
     static_libs: [
         "libprocinfo",
     ],
@@ -153,11 +157,10 @@
     shared_libs: [
         "libunwindstack",
     ],
-    relative_install_path: "libunwindstack_test",
 }
 
-cc_defaults {
-    name: "libunwindstack_testlib_flags",
+cc_test {
+    name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
 
     srcs: [
@@ -181,9 +184,8 @@
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
         "tests/ElfTestUtils.cpp",
-        "tests/IsolatedSettings.cpp",
         "tests/JitDebugTest.cpp",
-        "tests/LocalUpdatableMapsTest.cpp",
+        "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoCreateMemoryTest.cpp",
         "tests/MapInfoGetBuildIDTest.cpp",
@@ -211,7 +213,6 @@
         "tests/UnwindOfflineTest.cpp",
         "tests/UnwindTest.cpp",
         "tests/UnwinderTest.cpp",
-        "tests/VerifyBionicTerminationTest.cpp",
     ],
 
     cflags: [
@@ -239,15 +240,11 @@
         "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_bias_x86/*",
         "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/load_bias_different_section_bias_arm64/*",
-        "tests/files/offline/load_bias_ro_rx_x86_64/*",
         "tests/files/offline/offset_arm/*",
         "tests/files/offline/shared_lib_in_apk_arm64/*",
         "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
@@ -255,28 +252,11 @@
         "tests/files/offline/straddle_arm/*",
         "tests/files/offline/straddle_arm64/*",
     ],
-}
-
-cc_test {
-    name: "libunwindstack_test",
-    defaults: ["libunwindstack_testlib_flags"],
-    isolated: true,
-
-    srcs: [
-        "tests/LocalUnwinderTest.cpp",
-    ],
     required: [
         "libunwindstack_local",
     ],
 }
 
-// Skip LocalUnwinderTest until atest understands required properly.
-cc_test {
-    name: "libunwindstack_unit_test",
-    defaults: ["libunwindstack_testlib_flags"],
-    isolated: true,
-}
-
 //-------------------------------------------------------------------------
 // Tools
 //-------------------------------------------------------------------------
diff --git a/libunwindstack/AndroidVersions.md b/libunwindstack/AndroidVersions.md
deleted file mode 100644
index 234f639..0000000
--- a/libunwindstack/AndroidVersions.md
+++ /dev/null
@@ -1,116 +0,0 @@
-# Unwinder Support Per Android Release
-This document describes the changes in the way the libunwindstack
-unwinder works on different Android versions. It does not describe
-every change in the code made between different versions, but is
-meant to allow an app developer to know what might be supported
-on different versions. It also describes the different way an unwind
-will display on different versions of Android.
-
-## Android P
-libunwindstack was first introduced in Android P.
-
-* Supports up to and including Dwarf 4 unwinding information.
-  See http://dwarfstd.org/ for Dwarf standards.
-* Supports Arm exidx unwinding.
-* Supports the gdb JIT unwinding interface, which is how ART creates unwinding
-  information for the JIT'd Java frames.
-* Supports special frames added to represent an ART Java interpreter frame.
-  ART has marked the dex pc using cfi information that the unwinder
-  understands and handles by adding a new frame in the stacktrace.
-
-## Note
-By default, lld creates two separate maps of the elf in memory, one read-only
-and one read/executable. The libunwindstack on P and the unwinder on older
-versions of Android will not unwind properly in this case. For apps that
-target Android P or older, make sure that `-Wl,--no-rosegment` is
-included in linker arguments when using lld.
-
-## Android Q
-* Fix bug (b/109824792) that handled load bias data incorrectly when
-  FDEs use pc relative addressing in the eh\_frame\_hdr.
-  Unfortunately, this wasn't fixed correctly in Q since it assumes
-  that the bias is coming from the program header for the executable
-  load. The real fix was to use the bias from the actual section data and
-  is not completely fixed until Android R. For apps targeting Android Q,
-  if it is being compiled with the llvm linker lld, it might be necessary
-  to add the linker option `-Wl,-zseparate-code` to avoid creating an elf
-  created this way.
-* Change the way the exidx section offset is found (b/110704153). Before
-  the p\_vaddr value from the program header minus the load bias was used
-  to find the start of the exidx data. Changed to use the p\_offset since
-  it doesn't require any load bias manipulations.
-* Fix bug handling of dwarf sections without any header (b/110235461).
-  Previously, the code assumed that FDEs are non-overlapping, and the FDEs
-  are always in sorted order from low pc to high pc. Thus the code would
-  read the entire set of CIEs/FDEs and then do a binary search to find
-  the appropriate FDE for a given pc. Now the code does a sequential read
-  and stops when it finds the FDE for a pc. It also understands the
-  overlapping FDEs, so find the first FDE that matches a pc. In practice,
-  elf files with this format only ever occurs if the file was generated
-  without an eh\_frame/eh\_frame\_hdr section and only a debug\_frame. The
-  other way this has been observed is when running simpleperf to unwind since
-  sometimes there is not enough information in the eh\_frame for all points
-  in the executable. On Android P, this would result in some incorrect
-  unwinds coming from simpleperf. Nearly all crashes from Android P should
-  be correct since the eh\_frame information was enough to do the unwind
-  properly.
-* Be permissive of badly formed elf files. Previously, any detected error
-  would result in unwinds stopping even if there is enough valid information
-  to do an unwind.
-  * The code now allows program header/section header offsets to point
-    to unreadable memory. As long as the code can find the unwind tables,
-    that is good enough.
-  * The code allows program headers/section headers to be missing.
-  * Allow a symbol table section header to point to invalid symbol table
-    values.
-* Support for the linker read-only segment option (b/109657296).
-  This is a feature of lld whereby there are two sections that
-  contain elf data. The first is read-only and contains the elf header data,
-  and the second is read-execute or execute only that
-  contains the executable code from the elf. Before this, the unwinder
-  always assumed that there was only a single read-execute section that
-  contained the elf header data and the executable code.
-* Build ID information for elf objects added. This will display the
-  NT\_GNU\_BUILD\_ID note found in elf files. This information can be used
-  to identify the exact version of a shared library to help get symbol
-  information when looking at a crash.
-* Add support for displaying the soname from an apk frame. Previously,
-  a frame map name would be only the apk, but now if the shared library
-  in the apk has set a soname, the map name will be `app.apk!libexample.so`
-  instead of only `app.apk`.
-* Minimal support for Dwarf 5. This merely treats a Dwarf 5 version
-  elf file as Dwarf 4. It does not support the new dwarf ops in Dwarf 5.
-  Since the new ops are not likely to be used very often, this allows
-  continuing to unwind even when encountering Dwarf 5 elf files.
-* Fix bug in pc handling of signal frames (b/130302288). In the previous
-  version, the pc would be wrong in the signal frame. The rest of the
-  unwind was correct, only the frame in the signal handler was incorrect
-  in Android P.
-* Detect when an elf file is not readable so that a message can be
-  displayed indicating that. This can happen when an app puts the shared
-  libraries in non-standard locations that are not readable due to
-  security restrictions (selinux rules).
-
-## Android R
-* Display the offsets for Java interpreter frames. If this frame came
-  from a non-zero offset map, no offset is printed. Previously, the
-  line would look like:
-
-    #17 pc 00500d7a  GoogleCamera.apk (com.google.camera.AndroidPriorityThread.run+10)
-
-  to:
-
-    #17 pc 00500d7a  GoogleCamera.apk (offset 0x11d0000) (com.google.camera.AndroidPriorityThread.run+10)
-* Fix bug where the load bias was set from the first PT\_LOAD program
-  header that has a zero p\_offset value. Now it is set from the first
-  executable PT\_LOAD program header. This has only ever been a problem
-  for host executables compiled for the x86\_64 architecture.
-* Switched to the libc++ demangler for function names. Previously, the
-  demangler used was not complete, so some less common demangled function
-  names would not be properly demangled or the function name would not be
-  demangled at all.
-* Fix bug in load bias handling. If the unwind information in the eh\_frame
-  or eh\_frame\_hdr does not have the same bias as the executable section,
-  and uses pc relative FDEs, the unwind will be incorrect. This tends
-  to truncate unwinds since the unwinder could not find the correct unwind
-  information for a given pc.
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index dff7a8b..eaf867f 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -22,9 +22,6 @@
 
 #include <memory>
 
-#define LOG_TAG "unwind"
-#include <log/log.h>
-
 #include <android-base/unique_fd.h>
 #include <art_api/dex_file_support.h>
 
@@ -35,19 +32,6 @@
 
 namespace unwindstack {
 
-static bool CheckDexSupport() {
-  if (std::string err_msg; !art_api::dex::TryLoadLibdexfileExternal(&err_msg)) {
-    ALOGW("Failed to initialize DEX file support: %s", err_msg.c_str());
-    return false;
-  }
-  return true;
-}
-
-static bool HasDexSupport() {
-  static bool has_dex_support = CheckDexSupport();
-  return has_dex_support;
-}
-
 std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
                                          MapInfo* info) {
   if (!info->name.empty()) {
@@ -73,10 +57,6 @@
 
 std::unique_ptr<DexFileFromFile> DexFileFromFile::Create(uint64_t dex_file_offset_in_file,
                                                          const std::string& file) {
-  if (UNLIKELY(!HasDexSupport())) {
-    return nullptr;
-  }
-
   android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
   if (fd == -1) {
     return nullptr;
@@ -95,10 +75,6 @@
 std::unique_ptr<DexFileFromMemory> DexFileFromMemory::Create(uint64_t dex_file_offset_in_memory,
                                                              Memory* memory,
                                                              const std::string& name) {
-  if (UNLIKELY(!HasDexSupport())) {
-    return nullptr;
-  }
-
   std::vector<uint8_t> backing_memory;
 
   for (size_t size = 0;;) {
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 1358e51..802beca 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -32,19 +32,14 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::EhFrameInit(uint64_t offset, uint64_t size,
-                                                   int64_t section_bias) {
-  return DwarfSectionImpl<AddressType>::Init(offset, size, section_bias);
-}
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
+  load_bias_ = load_bias;
 
-template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t, int64_t section_bias) {
   memory_.clear_func_offset();
   memory_.clear_text_offset();
   memory_.set_data_offset(offset);
   memory_.set_cur_offset(offset);
-
-  hdr_section_bias_ = section_bias;
+  pc_offset_ = offset;
 
   // Read the first four bytes all at once.
   uint8_t data[4];
@@ -61,7 +56,7 @@
     return false;
   }
 
-  uint8_t ptr_encoding = data[1];
+  ptr_encoding_ = data[1];
   uint8_t fde_count_encoding = data[2];
   table_encoding_ = data[3];
   table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
@@ -75,8 +70,7 @@
   }
 
   memory_.set_pc_offset(memory_.cur_offset());
-  uint64_t ptr_offset;
-  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding, &ptr_offset)) {
+  if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
     last_error_.code = DWARF_ERROR_MEMORY_INVALID;
     last_error_.address = memory_.cur_offset();
     return false;
@@ -94,8 +88,10 @@
     return false;
   }
 
-  hdr_entries_offset_ = memory_.cur_offset();
-  hdr_entries_data_offset_ = offset;
+  entries_offset_ = memory_.cur_offset();
+  entries_end_ = offset + size;
+  entries_data_offset_ = offset;
+  cur_entries_offset_ = entries_offset_;
 
   return true;
 }
@@ -111,16 +107,6 @@
     return nullptr;
   }
 
-  // There is a possibility that this entry points to a zero length FDE
-  // due to a bug. If this happens, try and find the non-zero length FDE
-  // from eh_frame directly. See b/142483624.
-  if (fde->pc_start == fde->pc_end) {
-    fde = DwarfSectionImpl<AddressType>::GetFdeFromPc(pc);
-    if (fde == nullptr) {
-      return nullptr;
-    }
-  }
-
   // Guaranteed pc >= pc_start, need to check pc in the fde range.
   if (pc < fde->pc_end) {
     return fde;
@@ -138,8 +124,8 @@
   }
   FdeInfo* info = &fde_info_[index];
 
-  memory_.set_data_offset(hdr_entries_data_offset_);
-  memory_.set_cur_offset(hdr_entries_offset_ + 2 * index * table_entry_size_);
+  memory_.set_data_offset(entries_data_offset_);
+  memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
   memory_.set_pc_offset(0);
   uint64_t value;
   if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
@@ -152,7 +138,7 @@
 
   // Relative encodings require adding in the load bias.
   if (IsEncodingRelative(table_encoding_)) {
-    value += hdr_section_bias_;
+    value += load_bias_;
   }
   info->pc = value;
   return info;
@@ -204,16 +190,6 @@
     if (fde == nullptr) {
       break;
     }
-
-    // There is a possibility that this entry points to a zero length FDE
-    // due to a bug. If this happens, try and find the non-zero length FDE
-    // from eh_frame directly. See b/142483624.
-    if (fde->pc_start == fde->pc_end) {
-      const DwarfFde* fde_real = DwarfSectionImpl<AddressType>::GetFdeFromPc(fde->pc_start);
-      if (fde_real != nullptr) {
-        fde = fde_real;
-      }
-    }
     fdes->push_back(fde);
   }
 }
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index f7c010c..0e5eef7 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -34,7 +34,11 @@
   // Add these so that the protected members of DwarfSectionImpl
   // can be accessed without needing a this->.
   using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::pc_offset_;
+  using DwarfSectionImpl<AddressType>::entries_offset_;
+  using DwarfSectionImpl<AddressType>::entries_end_;
   using DwarfSectionImpl<AddressType>::last_error_;
+  using DwarfSectionImpl<AddressType>::load_bias_;
 
   struct FdeInfo {
     AddressType pc;
@@ -45,20 +49,19 @@
   virtual ~DwarfEhFrameWithHdr() = default;
 
   uint64_t GetCieOffsetFromFde32(uint32_t pointer) override {
-    return memory_.cur_offset() - pointer - 4;
+    return this->memory_.cur_offset() - pointer - 4;
   }
 
   uint64_t GetCieOffsetFromFde64(uint64_t pointer) override {
-    return memory_.cur_offset() - pointer - 8;
+    return this->memory_.cur_offset() - pointer - 8;
   }
 
   uint64_t AdjustPcFromFde(uint64_t pc) override {
     // The eh_frame uses relative pcs.
-    return pc + memory_.cur_offset() - 4;
+    return pc + this->memory_.cur_offset() - 4;
   }
 
-  bool EhFrameInit(uint64_t offset, uint64_t size, int64_t section_bias);
-  bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
 
   const DwarfFde* GetFdeFromPc(uint64_t pc) override;
 
@@ -69,15 +72,17 @@
   void GetFdes(std::vector<const DwarfFde*>* fdes) override;
 
  protected:
-  uint8_t version_ = 0;
-  uint8_t table_encoding_ = 0;
-  size_t table_entry_size_ = 0;
+  uint8_t version_;
+  uint8_t ptr_encoding_;
+  uint8_t table_encoding_;
+  size_t table_entry_size_;
 
-  uint64_t hdr_entries_offset_ = 0;
-  uint64_t hdr_entries_data_offset_ = 0;
-  uint64_t hdr_section_bias_ = 0;
+  uint64_t ptr_offset_;
 
-  uint64_t fde_count_ = 0;
+  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 2e388c6..b505900 100644
--- a/libunwindstack/DwarfMemory.cpp
+++ b/libunwindstack/DwarfMemory.cpp
@@ -111,7 +111,7 @@
       // Nothing to do.
       break;
     case DW_EH_PE_pcrel:
-      if (pc_offset_ == INT64_MAX) {
+      if (pc_offset_ == static_cast<uint64_t>(-1)) {
         // Unsupported encoding.
         return false;
       }
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index e6263f8..849a31a 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -69,7 +69,6 @@
     return &cie_entry->second;
   }
   DwarfCie* cie = &cie_entries_[offset];
-  memory_.set_data_offset(entries_offset_);
   memory_.set_cur_offset(offset);
   if (!FillInCieHeader(cie) || !FillInCie(cie)) {
     // Erase the cached entry.
@@ -252,7 +251,6 @@
     return &fde_entry->second;
   }
   DwarfFde* fde = &fde_entries_[offset];
-  memory_.set_data_offset(entries_offset_);
   memory_.set_cur_offset(offset);
   if (!FillInFdeHeader(fde) || !FillInFde(fde)) {
     fde_entries_.erase(offset);
@@ -335,7 +333,7 @@
   memory_.set_cur_offset(cur_offset);
 
   // The load bias only applies to the start.
-  memory_.set_pc_offset(section_bias_);
+  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);
 
@@ -593,8 +591,8 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Init(uint64_t offset, uint64_t size, int64_t section_bias) {
-  section_bias_ = section_bias;
+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;
@@ -602,6 +600,7 @@
   memory_.clear_func_offset();
   memory_.clear_text_offset();
   memory_.set_cur_offset(offset);
+  memory_.set_data_offset(offset);
   pc_offset_ = offset;
 
   return true;
@@ -617,7 +616,7 @@
 // and an fde has a start pc of 0x100 and end pc of 0x500, two new entries
 // will be added: 0x200, 0x100 and 0x500, 0x400.
 template <typename AddressType>
-void DwarfSectionImpl<AddressType>::InsertFde(const DwarfFde* fde) {
+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);
@@ -654,10 +653,9 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::GetNextCieOrFde(const DwarfFde** fde_entry) {
+bool DwarfSectionImplNoHdr<AddressType>::GetNextCieOrFde(DwarfFde** fde_entry) {
   uint64_t start_offset = next_entries_offset_;
 
-  memory_.set_data_offset(entries_offset_);
   memory_.set_cur_offset(next_entries_offset_);
   uint32_t value32;
   if (!memory_.ReadBytes(&value32, sizeof(value32))) {
@@ -690,7 +688,7 @@
       entry_is_cie = true;
       cie_fde_encoding = DW_EH_PE_sdata8;
     } else {
-      cie_offset = GetCieOffsetFromFde64(value64);
+      cie_offset = this->GetCieOffsetFromFde64(value64);
     }
   } else {
     next_entries_offset_ = memory_.cur_offset() + value32;
@@ -706,45 +704,37 @@
       entry_is_cie = true;
       cie_fde_encoding = DW_EH_PE_sdata4;
     } else {
-      cie_offset = GetCieOffsetFromFde32(value32);
+      cie_offset = this->GetCieOffsetFromFde32(value32);
     }
   }
 
   if (entry_is_cie) {
-    auto entry = cie_entries_.find(start_offset);
-    if (entry == cie_entries_.end()) {
-      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;
+    DwarfCie* cie = &cie_entries_[start_offset];
+    cie->lsda_encoding = DW_EH_PE_omit;
+    cie->cfa_instructions_end = next_entries_offset_;
+    cie->fde_address_encoding = cie_fde_encoding;
 
-      if (!FillInCie(cie)) {
-        cie_entries_.erase(start_offset);
-        return false;
-      }
+    if (!this->FillInCie(cie)) {
+      cie_entries_.erase(start_offset);
+      return false;
     }
     *fde_entry = nullptr;
   } else {
-    auto entry = fde_entries_.find(start_offset);
-    if (entry != fde_entries_.end()) {
-      *fde_entry = &entry->second;
-    } else {
-      DwarfFde* fde = &fde_entries_[start_offset];
-      fde->cfa_instructions_end = next_entries_offset_;
-      fde->cie_offset = cie_offset;
+    DwarfFde* fde = &fde_entries_[start_offset];
+    fde->cfa_instructions_end = next_entries_offset_;
+    fde->cie_offset = cie_offset;
 
-      if (!FillInFde(fde)) {
-        fde_entries_.erase(start_offset);
-        return false;
-      }
-      *fde_entry = fde;
+    if (!this->FillInFde(fde)) {
+      fde_entries_.erase(start_offset);
+      return false;
     }
+    *fde_entry = fde;
   }
   return true;
 }
 
 template <typename AddressType>
-void DwarfSectionImpl<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
+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_) {
@@ -763,7 +753,7 @@
   }
 
   while (next_entries_offset_ < entries_end_) {
-    const DwarfFde* fde;
+    DwarfFde* fde;
     if (!GetNextCieOrFde(&fde)) {
       break;
     }
@@ -780,7 +770,7 @@
 }
 
 template <typename AddressType>
-const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromPc(uint64_t pc) {
+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()) {
@@ -793,7 +783,7 @@
   // 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_) {
-    const DwarfFde* fde;
+    DwarfFde* fde;
     if (!GetNextCieOrFde(&fde)) {
       return nullptr;
     }
@@ -816,6 +806,10 @@
 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 c141b2e..3454913 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -53,7 +53,7 @@
 
   valid_ = interface_->Init(&load_bias_);
   if (valid_) {
-    interface_->InitHeaders();
+    interface_->InitHeaders(load_bias_);
     InitGnuDebugdata();
   } else {
     interface_.reset(nullptr);
@@ -77,9 +77,9 @@
 
   // Ignore the load_bias from the compressed section, the correct load bias
   // is in the uncompressed data.
-  int64_t load_bias;
+  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.
@@ -124,7 +124,7 @@
   }
 
   // Adjust by the load bias.
-  if (load_bias_ > 0 && *memory_address < static_cast<uint64_t>(load_bias_)) {
+  if (*memory_address < load_bias_) {
     return false;
   }
 
@@ -229,7 +229,7 @@
 }
 
 bool Elf::IsValidPc(uint64_t pc) {
-  if (!valid_ || (load_bias_ > 0 && pc < static_cast<uint64_t>(load_bias_))) {
+  if (!valid_ || pc < load_bias_) {
     return false;
   }
 
@@ -299,7 +299,7 @@
   return interface.release();
 }
 
-int64_t Elf::GetLoadBias(Memory* memory) {
+uint64_t Elf::GetLoadBias(Memory* memory) {
   if (!IsValidElf(memory)) {
     return 0;
   }
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 5f95fa8..dee8eb3 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 {
@@ -124,12 +124,10 @@
 }
 
 template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate() {
+void ElfInterface::InitHeadersWithTemplate(uint64_t load_bias) {
   if (eh_frame_hdr_offset_ != 0) {
-    DwarfEhFrameWithHdr<AddressType>* eh_frame_hdr = new DwarfEhFrameWithHdr<AddressType>(memory_);
-    eh_frame_.reset(eh_frame_hdr);
-    if (!eh_frame_hdr->EhFrameInit(eh_frame_offset_, eh_frame_size_, eh_frame_section_bias_) ||
-        !eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, eh_frame_hdr_section_bias_)) {
+    eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
+    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
       eh_frame_.reset(nullptr);
     }
   }
@@ -138,23 +136,21 @@
     // 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_, eh_frame_section_bias_)) {
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
       eh_frame_.reset(nullptr);
     }
   }
 
   if (eh_frame_.get() == nullptr) {
     eh_frame_hdr_offset_ = 0;
-    eh_frame_hdr_section_bias_ = 0;
     eh_frame_hdr_size_ = static_cast<uint64_t>(-1);
     eh_frame_offset_ = 0;
-    eh_frame_section_bias_ = 0;
     eh_frame_size_ = static_cast<uint64_t>(-1);
   }
 
   if (debug_frame_offset_ != 0) {
     debug_frame_.reset(new DwarfDebugFrame<AddressType>(memory_));
-    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, debug_frame_section_bias_)) {
+    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);
@@ -163,7 +159,7 @@
 }
 
 template <typename EhdrType, typename PhdrType, typename ShdrType>
-bool ElfInterface::ReadAllHeaders(int64_t* load_bias) {
+bool ElfInterface::ReadAllHeaders(uint64_t* load_bias) {
   EhdrType ehdr;
   if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
     last_error_.code = ERROR_MEMORY_INVALID;
@@ -179,7 +175,7 @@
 }
 
 template <typename EhdrType, typename PhdrType>
-int64_t ElfInterface::GetLoadBias(Memory* memory) {
+uint64_t ElfInterface::GetLoadBias(Memory* memory) {
   EhdrType ehdr;
   if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
     return false;
@@ -191,19 +187,16 @@
     if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
       return 0;
     }
-
-    // Find the first executable load when looking for the load bias.
-    if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) {
-      return static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
+    if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
+      return phdr.p_vaddr;
     }
   }
   return 0;
 }
 
 template <typename EhdrType, typename PhdrType>
-void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias) {
+void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
   uint64_t offset = ehdr.e_phoff;
-  bool first_exec_load_header = true;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
     if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
@@ -219,18 +212,15 @@
 
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
                                           static_cast<size_t>(phdr.p_memsz)};
-      // Only set the load bias from the first executable load header.
-      if (first_exec_load_header) {
-        *load_bias = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
+      if (phdr.p_offset == 0) {
+        *load_bias = phdr.p_vaddr;
       }
-      first_exec_load_header = false;
       break;
     }
 
     case PT_GNU_EH_FRAME:
       // This is really the pointer to the .eh_frame_hdr section.
       eh_frame_hdr_offset_ = phdr.p_offset;
-      eh_frame_hdr_section_bias_ = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
       eh_frame_hdr_size_ = phdr.p_memsz;
       break;
 
@@ -345,21 +335,24 @@
       if (shdr.sh_name < sec_size) {
         std::string name;
         if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
+          uint64_t* offset_ptr = nullptr;
+          uint64_t* size_ptr = nullptr;
           if (name == ".debug_frame") {
-            debug_frame_offset_ = shdr.sh_offset;
-            debug_frame_size_ = shdr.sh_size;
-            debug_frame_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
+            offset_ptr = &debug_frame_offset_;
+            size_ptr = &debug_frame_size_;
           } else if (name == ".gnu_debugdata") {
-            gnu_debugdata_offset_ = shdr.sh_offset;
-            gnu_debugdata_size_ = shdr.sh_size;
+            offset_ptr = &gnu_debugdata_offset_;
+            size_ptr = &gnu_debugdata_size_;
           } else if (name == ".eh_frame") {
-            eh_frame_offset_ = shdr.sh_offset;
-            eh_frame_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
-            eh_frame_size_ = shdr.sh_size;
+            offset_ptr = &eh_frame_offset_;
+            size_ptr = &eh_frame_size_;
           } else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
-            eh_frame_hdr_offset_ = shdr.sh_offset;
-            eh_frame_hdr_section_bias_ = static_cast<uint64_t>(shdr.sh_addr) - shdr.sh_offset;
-            eh_frame_hdr_size_ = shdr.sh_size;
+            offset_ptr = &eh_frame_hdr_offset_;
+            size_ptr = &eh_frame_hdr_size_;
+          }
+          if (offset_ptr != nullptr) {
+            *offset_ptr = shdr.sh_offset;
+            *size_ptr = shdr.sh_size;
           }
         }
       }
@@ -641,14 +634,16 @@
 }
 
 // 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>(int64_t*);
-template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(int64_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 void ElfInterface::ReadProgramHeaders<Elf32_Ehdr, Elf32_Phdr>(const Elf32_Ehdr&, int64_t*);
-template void ElfInterface::ReadProgramHeaders<Elf64_Ehdr, Elf64_Phdr>(const Elf64_Ehdr&, int64_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 void ElfInterface::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
 template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
@@ -670,8 +665,8 @@
 template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
 template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
 
-template int64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
-template int64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
+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*);
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 76f2dc8..3dd5d54 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -26,7 +26,7 @@
 
 namespace unwindstack {
 
-bool ElfInterfaceArm::Init(int64_t* load_bias) {
+bool ElfInterfaceArm::Init(uint64_t* load_bias) {
   if (!ElfInterface32::Init(load_bias)) {
     return false;
   }
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 1d71cac..4c3a0c3 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -64,7 +64,7 @@
   iterator begin() { return iterator(this, 0); }
   iterator end() { return iterator(this, total_entries_); }
 
-  bool Init(int64_t* section_bias) override;
+  bool Init(uint64_t* load_bias) override;
 
   bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
 
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index 8a85607..20bc4b9 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -23,8 +23,7 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/Maps.h>
-
-#include "MemoryRange.h"
+#include <unwindstack/Memory.h>
 
 // This implements the JIT Compilation Interface.
 // See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index f2dad84..03658b4 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <stdint.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -28,9 +27,7 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
-
-#include "MemoryFileAtOffset.h"
-#include "MemoryRange.h"
+#include <unwindstack/Memory.h>
 
 namespace unwindstack {
 
@@ -234,13 +231,11 @@
     }
   }
 
-  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.
+  // 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.
+  if (prev_map != nullptr && elf_start_offset != offset && prev_map->offset == elf_start_offset &&
+      prev_map->name == name) {
     std::lock_guard<std::mutex> guard(prev_map->mutex_);
     if (prev_map->elf.get() == nullptr) {
       prev_map->elf = elf;
@@ -264,8 +259,8 @@
 }
 
 uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
-  int64_t cur_load_bias = load_bias.load();
-  if (cur_load_bias != INT64_MAX) {
+  uint64_t cur_load_bias = load_bias.load();
+  if (cur_load_bias != static_cast<uint64_t>(-1)) {
     return cur_load_bias;
   }
 
@@ -301,7 +296,7 @@
 
 std::string MapInfo::GetBuildID() {
   uintptr_t id = build_id.load();
-  if (id != 0) {
+  if (build_id != 0) {
     return *reinterpret_cast<std::string*>(id);
   }
 
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 250e600..5da73e4 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -149,10 +149,9 @@
       }
 
       // Never delete these maps, they may be in use. The assumption is
-      // that there will only every be a handful of these so waiting
+      // 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));
-      search_map_idx = old_map_idx + 1;
       maps_[old_map_idx] = nullptr;
       total_entries--;
     }
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index a66cd5b..9904fef 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -32,14 +32,6 @@
 #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 {
 
@@ -176,16 +168,6 @@
   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());
@@ -200,11 +182,6 @@
   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;
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
deleted file mode 100644
index 3fe4bbb..0000000
--- a/libunwindstack/MemoryBuffer.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 769d907..0000000
--- a/libunwindstack/MemoryCache.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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
deleted file mode 100644
index d136eb4..0000000
--- a/libunwindstack/MemoryFileAtOffset.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 29aaf12..0000000
--- a/libunwindstack/MemoryLocal.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 789f1a2..0000000
--- a/libunwindstack/MemoryOffline.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 64c49a1..0000000
--- a/libunwindstack/MemoryOfflineBuffer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 3b4ab5c..0000000
--- a/libunwindstack/MemoryRange.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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
deleted file mode 100644
index db367d6..0000000
--- a/libunwindstack/MemoryRemote.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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/TEST_MAPPING b/libunwindstack/TEST_MAPPING
deleted file mode 100644
index 55771c0..0000000
--- a/libunwindstack/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "libunwindstack_unit_test"
-    }
-  ]
-}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 0b9b85c..37323dc 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -27,6 +27,8 @@
 #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>
@@ -38,9 +40,6 @@
 #include <unwindstack/DexFiles.h>
 #endif
 
-// Use the demangler from libc++.
-extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
-
 namespace unwindstack {
 
 // Inject extra 'virtual' frame that represents the dex pc data.
@@ -64,10 +63,7 @@
   if (info != nullptr) {
     frame->map_start = info->start;
     frame->map_end = info->end;
-    // 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_elf_start_offset = info->elf_start_offset;
     frame->map_exact_offset = info->offset;
     frame->map_load_bias = info->load_bias;
     frame->map_flags = info->flags;
@@ -309,7 +305,7 @@
   }
 }
 
-std::string Unwinder::FormatFrame(const FrameData& frame) const {
+std::string Unwinder::FormatFrame(const FrameData& frame) {
   std::string data;
   if (regs_->Is32Bit()) {
     data += android::base::StringPrintf("  #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
@@ -331,14 +327,7 @@
   }
 
   if (!frame.function_name.empty()) {
-    char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
-    if (demangled_name == nullptr) {
-      data += " (" + frame.function_name;
-    } else {
-      data += " (";
-      data += demangled_name;
-      free(demangled_name);
-    }
+    data += " (" + demangle(frame.function_name.c_str());
     if (frame.function_offset != 0) {
       data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
     }
@@ -355,7 +344,7 @@
   return data;
 }
 
-std::string Unwinder::FormatFrame(size_t frame_num) const {
+std::string Unwinder::FormatFrame(size_t frame_num) {
   if (frame_num >= frames_.size()) {
     return "";
   }
diff --git a/libunwindstack/include/unwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
index c45699a..8dd8d2b 100644
--- a/libunwindstack/include/unwindstack/DwarfMemory.h
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -49,8 +49,8 @@
   uint64_t cur_offset() { return cur_offset_; }
   void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
 
-  void set_pc_offset(int64_t offset) { pc_offset_ = offset; }
-  void clear_pc_offset() { pc_offset_ = INT64_MAX; }
+  void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
+  void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
 
   void set_data_offset(uint64_t offset) { data_offset_ = offset; }
   void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
@@ -65,7 +65,7 @@
   Memory* memory_;
   uint64_t cur_offset_ = 0;
 
-  int64_t pc_offset_ = INT64_MAX;
+  uint64_t pc_offset_ = static_cast<uint64_t>(-1);
   uint64_t data_offset_ = static_cast<uint64_t>(-1);
   uint64_t func_offset_ = static_cast<uint64_t>(-1);
   uint64_t text_offset_ = static_cast<uint64_t>(-1);
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index c244749..e9942de 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -86,7 +86,7 @@
   DwarfErrorCode LastErrorCode() { return last_error_.code; }
   uint64_t LastErrorAddress() { return last_error_.address; }
 
-  virtual bool Init(uint64_t offset, uint64_t size, int64_t section_bias) = 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;
 
@@ -125,16 +125,10 @@
   DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
   virtual ~DwarfSectionImpl() = default;
 
-  bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
-
   const DwarfCie* GetCieFromOffset(uint64_t offset);
 
   const DwarfFde* GetFdeFromOffset(uint64_t offset);
 
-  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
-
-  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
-
   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,
@@ -145,8 +139,6 @@
   bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
 
  protected:
-  bool GetNextCieOrFde(const DwarfFde** fde_entry);
-
   bool FillInCieHeader(DwarfCie* cie);
 
   bool FillInCie(DwarfCie* cie);
@@ -158,13 +150,43 @@
   bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
                       RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
 
-  void InsertFde(const DwarfFde* fde);
-
-  int64_t section_bias_ = 0;
+  uint64_t load_bias_ = 0;
   uint64_t entries_offset_ = 0;
   uint64_t entries_end_ = 0;
-  uint64_t next_entries_offset_ = 0;
   uint64_t pc_offset_ = 0;
+};
+
+template <typename AddressType>
+class DwarfSectionImplNoHdr : public DwarfSectionImpl<AddressType> {
+ public:
+  // Add these so that the protected members of DwarfSectionImpl
+  // can be accessed without needing a this->.
+  using DwarfSectionImpl<AddressType>::memory_;
+  using DwarfSectionImpl<AddressType>::pc_offset_;
+  using DwarfSectionImpl<AddressType>::entries_offset_;
+  using DwarfSectionImpl<AddressType>::entries_end_;
+  using DwarfSectionImpl<AddressType>::last_error_;
+  using DwarfSectionImpl<AddressType>::load_bias_;
+  using DwarfSectionImpl<AddressType>::cie_entries_;
+  using DwarfSectionImpl<AddressType>::fde_entries_;
+  using DwarfSectionImpl<AddressType>::cie32_value_;
+  using DwarfSectionImpl<AddressType>::cie64_value_;
+
+  DwarfSectionImplNoHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
+  virtual ~DwarfSectionImplNoHdr() = default;
+
+  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+
+  const DwarfFde* GetFdeFromPc(uint64_t pc) override;
+
+  void GetFdes(std::vector<const DwarfFde*>* fdes) override;
+
+ protected:
+  bool GetNextCieOrFde(DwarfFde** fde_entry);
+
+  void InsertFde(const DwarfFde* fde);
+
+  uint64_t next_entries_offset_ = 0;
 
   std::map<uint64_t, std::pair<uint64_t, const DwarfFde*>> fdes_;
 };
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index fc3f2a6..56bf318 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -75,7 +75,7 @@
 
   std::string GetBuildID();
 
-  int64_t GetLoadBias() { return load_bias_; }
+  uint64_t GetLoadBias() { return load_bias_; }
 
   bool IsValidPc(uint64_t pc);
 
@@ -101,7 +101,7 @@
 
   static bool GetInfo(Memory* memory, uint64_t* size);
 
-  static int64_t GetLoadBias(Memory* memory);
+  static uint64_t GetLoadBias(Memory* memory);
 
   static std::string GetBuildID(Memory* memory);
 
@@ -116,7 +116,7 @@
 
  protected:
   bool valid_ = false;
-  int64_t load_bias_ = 0;
+  uint64_t load_bias_ = 0;
   std::unique_ptr<ElfInterface> interface_;
   std::unique_ptr<Memory> memory_;
   uint32_t machine_type_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index ae9bd9a..dbd917d 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -52,9 +52,9 @@
   ElfInterface(Memory* memory) : memory_(memory) {}
   virtual ~ElfInterface();
 
-  virtual bool Init(int64_t* load_bias) = 0;
+  virtual bool Init(uint64_t* load_bias) = 0;
 
-  virtual void InitHeaders() = 0;
+  virtual void InitHeaders(uint64_t load_bias) = 0;
 
   virtual std::string GetSoname() = 0;
 
@@ -80,13 +80,10 @@
   uint64_t dynamic_vaddr() { return dynamic_vaddr_; }
   uint64_t dynamic_size() { return dynamic_size_; }
   uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
-  int64_t eh_frame_hdr_section_bias() { return eh_frame_hdr_section_bias_; }
   uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
   uint64_t eh_frame_offset() { return eh_frame_offset_; }
-  int64_t eh_frame_section_bias() { return eh_frame_section_bias_; }
   uint64_t eh_frame_size() { return eh_frame_size_; }
   uint64_t debug_frame_offset() { return debug_frame_offset_; }
-  int64_t debug_frame_section_bias() { return debug_frame_section_bias_; }
   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_; }
@@ -101,20 +98,20 @@
   uint64_t LastErrorAddress() { return last_error_.address; }
 
   template <typename EhdrType, typename PhdrType>
-  static int64_t GetLoadBias(Memory* memory);
+  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(int64_t* load_bias);
+  bool ReadAllHeaders(uint64_t* load_bias);
 
   template <typename EhdrType, typename PhdrType>
-  void ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias);
+  void ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
 
   template <typename EhdrType, typename ShdrType>
   void ReadSectionHeaders(const EhdrType& ehdr);
@@ -145,15 +142,12 @@
   uint64_t dynamic_size_ = 0;
 
   uint64_t eh_frame_hdr_offset_ = 0;
-  int64_t eh_frame_hdr_section_bias_ = 0;
   uint64_t eh_frame_hdr_size_ = 0;
 
   uint64_t eh_frame_offset_ = 0;
-  int64_t eh_frame_section_bias_ = 0;
   uint64_t eh_frame_size_ = 0;
 
   uint64_t debug_frame_offset_ = 0;
-  int64_t debug_frame_section_bias_ = 0;
   uint64_t debug_frame_size_ = 0;
 
   uint64_t gnu_debugdata_offset_ = 0;
@@ -181,11 +175,13 @@
   ElfInterface32(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterface32() = default;
 
-  bool Init(int64_t* load_bias) override {
+  bool Init(uint64_t* load_bias) override {
     return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
   }
 
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
+  }
 
   std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
 
@@ -209,11 +205,13 @@
   ElfInterface64(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterface64() = default;
 
-  bool Init(int64_t* load_bias) override {
+  bool Init(uint64_t* load_bias) override {
     return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
   }
 
-  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
+  void InitHeaders(uint64_t load_bias) override {
+    ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
+  }
 
   std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
 
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 8f0c516..025fd98 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -25,11 +25,10 @@
 #include <string>
 
 #include <unwindstack/Elf.h>
+#include <unwindstack/Memory.h>
 
 namespace unwindstack {
 
-class MemoryFileAtOffset;
-
 struct MapInfo {
   MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
           const char* name)
@@ -39,7 +38,7 @@
         flags(flags),
         name(name),
         prev_map(map_info),
-        load_bias(INT64_MAX),
+        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)
@@ -49,7 +48,7 @@
         flags(flags),
         name(name),
         prev_map(map_info),
-        load_bias(INT64_MAX),
+        load_bias(static_cast<uint64_t>(-1)),
         build_id(0) {}
   ~MapInfo();
 
@@ -59,20 +58,17 @@
   uint16_t flags = 0;
   std::string name;
   std::shared_ptr<Elf> elf;
-  // The offset of the beginning of this mapping to the beginning of the
-  // ELF file.
-  // elf_offset == offset - elf_start_offset.
   // This value is only non-zero if the offset is non-zero but there is
   // no elf signature found at that offset.
   uint64_t elf_offset = 0;
-  // This value is the offset into the file of the map in memory that is the
-  // start of the elf. This is not equal to offset when the linker splits
+  // 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_int64_t load_bias;
+  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
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index e53f367..1784394 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -105,7 +105,7 @@
 
   const std::string GetMapsFile() const override;
 
- protected:
+ private:
   std::vector<std::unique_ptr<MapInfo>> saved_maps_;
 };
 
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 3106564..b3beb6e 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -21,8 +21,12 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <atomic>
+#include <map>
 #include <memory>
 #include <string>
+#include <unordered_map>
+#include <vector>
 
 namespace unwindstack {
 
@@ -33,9 +37,6 @@
 
   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);
 
@@ -54,6 +55,157 @@
   }
 };
 
+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_;
+};
+
+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() override;
+
+ 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;
+
+  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_;
+};
+
+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/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 11ad9de..52b3578 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -77,7 +77,7 @@
   void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
               const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
 
-  size_t NumFrames() const { return frames_.size(); }
+  size_t NumFrames() { return frames_.size(); }
 
   const std::vector<FrameData>& frames() { return frames_; }
 
@@ -87,8 +87,8 @@
     return frames;
   }
 
-  std::string FormatFrame(size_t frame_num) const;
-  std::string FormatFrame(const FrameData& frame) const;
+  std::string FormatFrame(size_t frame_num);
+  std::string FormatFrame(const FrameData& frame);
 
   void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
 
diff --git a/libunwindstack/tests/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 69a7816..5f3d1ea 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -1662,7 +1662,7 @@
   ASSERT_EQ(0x10U, (*exidx_->regs())[15]);
 }
 
-INSTANTIATE_TEST_SUITE_P(Unwindstack, ArmExidxDecodeTest,
-                         ::testing::Values("logging", "register_logging", "no_logging"));
+INSTANTIATE_TEST_CASE_P(, ArmExidxDecodeTest,
+                        ::testing::Values("logging", "register_logging", "no_logging"));
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 9dd0cdd..bb2e8f0 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -66,7 +66,7 @@
   DwarfCie cie_;
   DwarfFde fde_;
 };
-TYPED_TEST_SUITE_P(DwarfCfaLogTest);
+TYPED_TEST_CASE_P(DwarfCfaLogTest);
 
 // NOTE: All class variable references have to be prefaced with this->.
 
@@ -763,17 +763,17 @@
   ASSERT_EQ("", GetFakeLogBuf());
 }
 
-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);
+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);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index dd71490..7395b04 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -64,7 +64,7 @@
   DwarfCie cie_;
   DwarfFde fde_;
 };
-TYPED_TEST_SUITE_P(DwarfCfaTest);
+TYPED_TEST_CASE_P(DwarfCfaTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
@@ -952,17 +952,16 @@
   ASSERT_EQ("", GetFakeLogBuf());
 }
 
-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);
+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);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfCfaTest, DwarfCfaTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 2b36f17..120bd73 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -44,7 +44,7 @@
   MemoryFake memory_;
   DwarfDebugFrame<TypeParam>* debug_frame_ = nullptr;
 };
-TYPED_TEST_SUITE_P(DwarfDebugFrameTest);
+TYPED_TEST_CASE_P(DwarfDebugFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
@@ -812,7 +812,7 @@
   EXPECT_EQ(0xb50U, fde->pc_end);
 }
 
-REGISTER_TYPED_TEST_SUITE_P(
+REGISTER_TYPED_TEST_CASE_P(
     DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
     GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
     GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
@@ -825,6 +825,6 @@
     GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 4792fb5..9cac6e8 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -42,7 +42,7 @@
   MemoryFake memory_;
   DwarfEhFrame<TypeParam>* eh_frame_ = nullptr;
 };
-TYPED_TEST_SUITE_P(DwarfEhFrameTest);
+TYPED_TEST_CASE_P(DwarfEhFrameTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
@@ -125,9 +125,9 @@
   EXPECT_EQ(1U, cie->return_address_register);
 }
 
-REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, GetFdeCieFromOffset32, GetFdeCieFromOffset64);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfEhFrameTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 768a808..be9e721 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -36,8 +36,10 @@
   ~TestDwarfEhFrameWithHdr() = default;
 
   void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; }
-  void TestSetHdrEntriesOffset(uint64_t offset) { this->hdr_entries_offset_ = offset; }
-  void TestSetHdrEntriesDataOffset(uint64_t offset) { this->hdr_entries_data_offset_ = offset; }
+  void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; }
+  void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; }
+  void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; }
+  void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; }
   void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; }
 
   void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; }
@@ -46,11 +48,15 @@
   }
 
   uint8_t TestGetVersion() { return this->version_; }
+  uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; }
+  uint64_t TestGetPtrOffset() { return this->ptr_offset_; }
   uint8_t TestGetTableEncoding() { return this->table_encoding_; }
   uint64_t TestGetTableEntrySize() { return this->table_entry_size_; }
   uint64_t TestGetFdeCount() { return this->fde_count_; }
-  uint64_t TestGetHdrEntriesOffset() { return this->hdr_entries_offset_; }
-  uint64_t TestGetHdrEntriesDataOffset() { return this->hdr_entries_data_offset_; }
+  uint64_t TestGetEntriesOffset() { return this->entries_offset_; }
+  uint64_t TestGetEntriesEnd() { return this->entries_end_; }
+  uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; }
+  uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; }
 };
 
 template <typename TypeParam>
@@ -67,7 +73,7 @@
   MemoryFake memory_;
   TestDwarfEhFrameWithHdr<TypeParam>* eh_frame_ = nullptr;
 };
-TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest);
+TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
@@ -79,11 +85,15 @@
 
   ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
   EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
+  EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding());
   EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding());
   EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
   EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
-  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
+  EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset());
+  EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd());
+  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset());
+  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset());
 
   // Verify a zero table entry size fails to init.
   this->memory_.SetData8(0x1003, 0x1);
@@ -127,14 +137,17 @@
   this->memory_.SetData32(0x140c, 0x200);
   this->memory_.SetData16(0x1410, 0);
 
-  ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x200, 0x2000));
   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(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
-  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
+  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);
@@ -142,115 +155,6 @@
   EXPECT_EQ(0x4700U, fde->pc_end);
 }
 
-TYPED_TEST_P(DwarfEhFrameWithHdrTest, Init_non_zero_load_bias_different_from_eh_frame_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, 0x20f8);
-  this->memory_.SetData32(0x140c, 0x200);
-  this->memory_.SetData16(0x1410, 0);
-
-  ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x200, 0x1000));
-  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0x2000));
-  EXPECT_EQ(1U, this->eh_frame_->TestGetVersion());
-  EXPECT_EQ(0x1b, this->eh_frame_->TestGetTableEncoding());
-  EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize());
-  EXPECT_EQ(1U, this->eh_frame_->TestGetFdeCount());
-  EXPECT_EQ(0x100aU, this->eh_frame_->TestGetHdrEntriesOffset());
-  EXPECT_EQ(0x1000U, this->eh_frame_->TestGetHdrEntriesDataOffset());
-
-  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, GetFdeFromPc_wtih_empty_fde) {
-  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, 0x30f8);
-  this->memory_.SetData32(0x140c, 0);
-  this->memory_.SetData16(0x1410, 0);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x1500, 0xfc);
-  this->memory_.SetData32(0x1504, 0x204);
-  this->memory_.SetData32(0x1508, 0x2ff8);
-  this->memory_.SetData32(0x150c, 0x200);
-  this->memory_.SetData16(0x1510, 0);
-
-  ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x300, 0));
-  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
-
-  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_with_empty_fde) {
-  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, 0x30f8);
-  this->memory_.SetData32(0x140c, 0);
-  this->memory_.SetData16(0x1410, 0);
-
-  // FDE 32 information.
-  this->memory_.SetData32(0x1500, 0xfc);
-  this->memory_.SetData32(0x1504, 0x204);
-  this->memory_.SetData32(0x1508, 0x2ff8);
-  this->memory_.SetData32(0x150c, 0x200);
-  this->memory_.SetData16(0x1510, 0);
-
-  ASSERT_TRUE(this->eh_frame_->EhFrameInit(0x1300, 0x300, 0));
-  ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100, 0));
-
-  std::vector<const DwarfFde*> fdes;
-  this->eh_frame_->GetFdes(&fdes);
-  ASSERT_FALSE(fdes.empty());
-  ASSERT_EQ(1U, fdes.size());
-  EXPECT_EQ(0x4500U, fdes[0]->pc_start);
-  EXPECT_EQ(0x4700U, fdes[0]->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});
@@ -316,7 +220,7 @@
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_expect_cache_fail) {
   this->eh_frame_->TestSetTableEntrySize(0x10);
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
 
   ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr);
   ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->LastErrorCode());
@@ -329,8 +233,8 @@
 // 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_->TestSetHdrEntriesOffset(0x1000);
-  this->eh_frame_->TestSetHdrEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
   this->eh_frame_->TestSetTableEntrySize(0x10);
 
   this->memory_.SetData32(0x1040, 0x340);
@@ -344,8 +248,8 @@
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_read_datarel) {
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4);
-  this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
-  this->eh_frame_->TestSetHdrEntriesDataOffset(0x3000);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesDataOffset(0x3000);
   this->eh_frame_->TestSetTableEntrySize(0x10);
 
   this->memory_.SetData32(0x1040, 0x340);
@@ -359,7 +263,7 @@
 
 TYPED_TEST_P(DwarfEhFrameWithHdrTest, GetFdeInfoFromIndex_cached) {
   this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4);
-  this->eh_frame_->TestSetHdrEntriesOffset(0x1000);
+  this->eh_frame_->TestSetEntriesOffset(0x1000);
   this->eh_frame_->TestSetTableEntrySize(0x10);
 
   this->memory_.SetData32(0x1040, 0x340);
@@ -542,16 +446,14 @@
   ASSERT_EQ(nullptr, this->eh_frame_->GetFdeFromPc(0x800));
 }
 
-REGISTER_TYPED_TEST_SUITE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias,
-                            Init_non_zero_load_bias_different_from_eh_frame_bias,
-                            GetFdeFromPc_wtih_empty_fde, GetFdes_with_empty_fde, 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);
+REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameWithHdrTest, Init, Init_non_zero_load_bias, GetFdes,
+                           GetFdeInfoFromIndex_expect_cache_fail, GetFdeInfoFromIndex_read_pcrel,
+                           GetFdeInfoFromIndex_read_datarel, GetFdeInfoFromIndex_cached,
+                           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_SUITE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameWithHdrTest, DwarfEhFrameWithHdrTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index f4ade5d..3f09dd8 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_SUITE_P(DwarfOpLogTest);
+TYPED_TEST_CASE_P(DwarfOpLogTest);
 
 TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
   // Multi operation opcodes.
@@ -65,9 +65,9 @@
   ASSERT_EQ(expected, lines);
 }
 
-REGISTER_TYPED_TEST_SUITE_P(DwarfOpLogTest, multiple_ops);
+REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 0898ec0..d424d5f 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_SUITE_P(DwarfOpTest);
+TYPED_TEST_CASE_P(DwarfOpTest);
 
 TYPED_TEST_P(DwarfOpTest, decode) {
   // Memory error.
@@ -1571,16 +1571,15 @@
   EXPECT_FALSE(this->op_->dex_pc_set());
 }
 
-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);
+REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, not_implemented, op_addr,
+                           op_deref, op_deref_size, const_unsigned, const_signed, const_uleb,
+                           const_sleb, op_dup, op_drop, op_over, op_pick, op_swap, op_rot, op_abs,
+                           op_and, op_div, op_minus, op_mod, op_mul, op_neg, op_not, op_or, op_plus,
+                           op_plus_uconst, op_shl, op_shr, op_shra, op_xor, op_bra,
+                           compare_opcode_stack_error, compare_opcodes, op_skip, op_lit, op_reg,
+                           op_regx, op_breg, op_breg_invalid_register, op_bregx, op_nop, is_dex_pc);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfOpTest, DwarfOpTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index a9d6dad..46f555a 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -35,7 +35,7 @@
   TestDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
   virtual ~TestDwarfSectionImpl() = default;
 
-  bool Init(uint64_t, uint64_t, int64_t) override { return false; }
+  bool Init(uint64_t, uint64_t, uint64_t) override { return false; }
 
   void GetFdes(std::vector<const DwarfFde*>*) override {}
 
@@ -68,7 +68,7 @@
   MemoryFake memory_;
   TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
 };
-TYPED_TEST_SUITE_P(DwarfSectionImplTest);
+TYPED_TEST_CASE_P(DwarfSectionImplTest);
 
 // NOTE: All test class variables need to be referenced as this->.
 
@@ -571,18 +571,18 @@
   ASSERT_EQ("", GetFakeLogBuf());
 }
 
-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);
+REGISTER_TYPED_TEST_CASE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_not_cache,
+                           GetFdeFromOffset_fail_should_not_cache, Eval_cfa_expr_eval_fail,
+                           Eval_cfa_expr_no_stack, Eval_cfa_expr_is_register, Eval_cfa_expr,
+                           Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa, Eval_cfa_bad,
+                           Eval_cfa_register_prev, Eval_cfa_register_from_value,
+                           Eval_double_indirection, Eval_register_reference_chain, Eval_dex_pc,
+                           Eval_invalid_register, Eval_different_reg_locations,
+                           Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
+                           Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+                           GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
 
 typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
-INSTANTIATE_TYPED_TEST_SUITE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 953dc75..d754fcc 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,24 +30,23 @@
   MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
   virtual ~MockDwarfSection() = default;
 
-  MOCK_METHOD(bool, Init, (uint64_t, uint64_t, int64_t), (override));
+  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
 
-  MOCK_METHOD(bool, Eval, (const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*),
-              (override));
+  MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
 
-  MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*), (override));
+  MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
 
-  MOCK_METHOD(void, GetFdes, (std::vector<const DwarfFde*>*), (override));
+  MOCK_METHOD1(GetFdes, void(std::vector<const DwarfFde*>*));
 
-  MOCK_METHOD(const DwarfFde*, GetFdeFromPc, (uint64_t), (override));
+  MOCK_METHOD1(GetFdeFromPc, const DwarfFde*(uint64_t));
 
-  MOCK_METHOD(bool, GetCfaLocationInfo, (uint64_t, const DwarfFde*, dwarf_loc_regs_t*), (override));
+  MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
 
-  MOCK_METHOD(uint64_t, GetCieOffsetFromFde32, (uint32_t), (override));
+  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
 
-  MOCK_METHOD(uint64_t, GetCieOffsetFromFde64, (uint64_t), (override));
+  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
 
-  MOCK_METHOD(uint64_t, AdjustPcFromFde, (uint64_t), (override));
+  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
 };
 
 class DwarfSectionTest : public ::testing::Test {
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 5735858..07fd6f6 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -31,7 +31,7 @@
 
 class ElfCacheTest : public ::testing::Test {
  protected:
-  static void SetUpTestSuite() { memory_.reset(new MemoryFake); }
+  static void SetUpTestCase() { memory_.reset(new MemoryFake); }
 
   void SetUp() override { Elf::SetCachingEnabled(true); }
 
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 832e64a..bd3083c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -66,8 +66,8 @@
   ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterfaceFake() = default;
 
-  bool Init(int64_t*) override { return false; }
-  void InitHeaders() override {}
+  bool Init(uint64_t*) override { return false; }
+  void InitHeaders(uint64_t) override {}
   std::string GetSoname() override { return fake_soname_; }
 
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index ea27e3e..cdc927a 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -112,21 +112,6 @@
   template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
   void InitSectionHeadersOffsets();
 
-  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
-  void InitSectionHeadersOffsetsEhFrameSectionBias(uint64_t addr, uint64_t offset,
-                                                   int64_t expected_bias);
-
-  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
-  void InitSectionHeadersOffsetsEhFrameHdrSectionBias(uint64_t addr, uint64_t offset,
-                                                      int64_t expected_bias);
-
-  template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
-  void InitSectionHeadersOffsetsDebugFrameSectionBias(uint64_t addr, uint64_t offset,
-                                                      int64_t expected_bias);
-
-  template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
-  void CheckGnuEhFrame(uint64_t addr, uint64_t offset, int64_t expected_bias);
-
   template <typename Sym>
   void InitSym(uint64_t offset, uint32_t value, uint32_t size, uint32_t name_offset,
                uint64_t sym_offset, const char* name);
@@ -146,12 +131,6 @@
   template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
   void BuildIDSectionTooSmallForHeader();
 
-  template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
-  void CheckLoadBiasInFirstPhdr(int64_t load_bias);
-
-  template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
-  void CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr, int64_t load_bias);
-
   MemoryFake memory_;
 };
 
@@ -187,9 +166,9 @@
   phdr.p_align = 0x1000;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000, load_bias);
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -199,11 +178,11 @@
   ASSERT_EQ(0x10000U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, single_pt_load_32) {
+TEST_F(ElfInterfaceTest, elf32_single_pt_load) {
   SinglePtLoad<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, single_pt_load_64) {
+TEST_F(ElfInterfaceTest, elf64_single_pt_load) {
   SinglePtLoad<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
 
@@ -243,9 +222,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000, load_bias);
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(3U, pt_loads.size());
@@ -266,11 +245,11 @@
   ASSERT_EQ(0x10002U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_32) {
+TEST_F(ElfInterfaceTest, elf32_multiple_executable_pt_loads) {
   MultipleExecutablePtLoads<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_64) {
+TEST_F(ElfInterfaceTest, elf64_multiple_executable_pt_loads) {
   MultipleExecutablePtLoads<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
 
@@ -310,9 +289,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * (sizeof(phdr) + 100), &phdr, sizeof(phdr));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000, load_bias);
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(3U, pt_loads.size());
@@ -333,12 +312,12 @@
   ASSERT_EQ(0x10002U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_increments_not_size_of_phdr_32) {
+TEST_F(ElfInterfaceTest, elf32_multiple_executable_pt_loads_increments_not_size_of_phdr) {
   MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn,
                                                    ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_increments_not_size_of_phdr_64) {
+TEST_F(ElfInterfaceTest, elf64_multiple_executable_pt_loads_increments_not_size_of_phdr) {
   MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn,
                                                    ElfInterface64>();
 }
@@ -379,9 +358,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x1001, load_bias);
+  EXPECT_EQ(0U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -392,11 +371,11 @@
   ASSERT_EQ(0x10001U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, non_executable_pt_loads_32) {
+TEST_F(ElfInterfaceTest, elf32_non_executable_pt_loads) {
   NonExecutablePtLoads<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, non_executable_pt_loads_64) {
+TEST_F(ElfInterfaceTest, elf64_non_executable_pt_loads) {
   NonExecutablePtLoads<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
 
@@ -449,10 +428,11 @@
   memset(&phdr, 0, sizeof(phdr));
   phdr.p_type = PT_GNU_EH_FRAME;
   memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
+  phdr_offset += sizeof(phdr);
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000, load_bias);
+  EXPECT_EQ(0x2000U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -463,15 +443,15 @@
   ASSERT_EQ(0x10000U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, many_phdrs_32) {
+TEST_F(ElfInterfaceTest, elf32_many_phdrs) {
   ElfInterfaceTest::ManyPhdrs<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, many_phdrs_64) {
+TEST_F(ElfInterfaceTest, elf64_many_phdrs) {
   ElfInterfaceTest::ManyPhdrs<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
 
-TEST_F(ElfInterfaceTest, arm32) {
+TEST_F(ElfInterfaceTest, elf32_arm) {
   ElfInterfaceArm elf_arm(&memory_);
 
   Elf32_Ehdr ehdr = {};
@@ -490,9 +470,9 @@
   memory_.SetData32(0x2000, 0x1000);
   memory_.SetData32(0x2008, 0x1000);
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf_arm.Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
 
   std::vector<uint32_t> entries;
   for (auto addr : elf_arm) {
@@ -571,19 +551,19 @@
 void ElfInterfaceTest::Soname() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
 
   ASSERT_EQ("fake_soname.so", elf->GetSoname());
 }
 
-TEST_F(ElfInterfaceTest, soname_32) {
+TEST_F(ElfInterfaceTest, elf32_soname) {
   SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>();
   Soname<ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, soname_64) {
+TEST_F(ElfInterfaceTest, elf64_soname) {
   SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>();
   Soname<ElfInterface64>();
 }
@@ -592,19 +572,19 @@
 void ElfInterfaceTest::SonameAfterDtNull() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
 
   ASSERT_EQ("", elf->GetSoname());
 }
 
-TEST_F(ElfInterfaceTest, soname_after_dt_null_32) {
+TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
   SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTNULL_AFTER);
   SonameAfterDtNull<ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, soname_after_dt_null_64) {
+TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) {
   SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTNULL_AFTER);
   SonameAfterDtNull<ElfInterface64>();
 }
@@ -613,19 +593,19 @@
 void ElfInterfaceTest::SonameSize() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
 
   ASSERT_EQ("", elf->GetSoname());
 }
 
-TEST_F(ElfInterfaceTest, soname_size_32) {
+TEST_F(ElfInterfaceTest, elf32_soname_size) {
   SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTSIZE_SMALL);
   SonameSize<ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, soname_size_64) {
+TEST_F(ElfInterfaceTest, elf64_soname_size) {
   SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTSIZE_SMALL);
   SonameSize<ElfInterface64>();
 }
@@ -636,19 +616,19 @@
 void ElfInterfaceTest::SonameMissingMap() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
 
   ASSERT_EQ("", elf->GetSoname());
 }
 
-TEST_F(ElfInterfaceTest, soname_missing_map_32) {
+TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
   SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_MISSING_MAP);
   SonameMissingMap<ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, soname_missing_map_64) {
+TEST_F(ElfInterfaceTest, elf64_soname_missing_map) {
   SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_MISSING_MAP);
   SonameMissingMap<ElfInterface64>();
 }
@@ -667,17 +647,17 @@
   memory_.SetData32(0x10004, 0x500);
   memory_.SetData32(0x10008, 250);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_FALSE(elf.eh_frame() == nullptr);
   EXPECT_TRUE(elf.debug_frame() == nullptr);
 }
 
-TEST_F(ElfInterfaceTest, init_headers_eh_frame_32) {
+TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
   InitHeadersEhFrameTest<ElfInterface32Fake>();
 }
 
-TEST_F(ElfInterfaceTest, init_headers_eh_frame_64) {
+TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
   InitHeadersEhFrameTest<ElfInterface64Fake>();
 }
 
@@ -699,17 +679,17 @@
   memory_.SetData32(0x5108, 0x1500);
   memory_.SetData32(0x510c, 0x200);
 
-  elf.InitHeaders();
+  elf.InitHeaders(0);
 
   EXPECT_TRUE(elf.eh_frame() == nullptr);
   EXPECT_FALSE(elf.debug_frame() == nullptr);
 }
 
-TEST_F(ElfInterfaceTest, init_headers_debug_frame_32) {
+TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
   InitHeadersDebugFrame<ElfInterface32Fake>();
 }
 
-TEST_F(ElfInterfaceTest, init_headers_debug_frame_64) {
+TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
   InitHeadersDebugFrame<ElfInterface64Fake>();
 }
 
@@ -723,16 +703,16 @@
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
 }
 
-TEST_F(ElfInterfaceTest, init_program_headers_malformed_32) {
+TEST_F(ElfInterfaceTest, init_program_headers_malformed32) {
   InitProgramHeadersMalformed<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_program_headers_malformed_64) {
+TEST_F(ElfInterfaceTest, init_program_headers_malformed64) {
   InitProgramHeadersMalformed<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>();
 }
 
@@ -746,16 +726,16 @@
   ehdr.e_shentsize = sizeof(Shdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_malformed_32) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
   InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_malformed_64) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed64) {
   InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
 
@@ -810,10 +790,11 @@
   shdr.sh_offset = 0xf000;
   shdr.sh_size = 0x1000;
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, 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());
@@ -824,11 +805,11 @@
   ASSERT_FALSE(elf->GetFunctionName(0x90010, &name, &name_offset));
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata_32) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata32) {
   InitSectionHeadersMalformedSymData<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata_64) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata64) {
   InitSectionHeadersMalformedSymData<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
 
@@ -879,13 +860,14 @@
   shdr.sh_offset = 0xf000;
   shdr.sh_size = 0x1000;
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
+  offset += ehdr.e_shentsize;
 
   InitSym<Sym>(0x5000, 0x90000, 0x1000, 0x100, 0xf000, "function_one");
   InitSym<Sym>(0x6000, 0xd0000, 0x1000, 0x300, 0xf000, "function_two");
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, 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());
@@ -902,19 +884,19 @@
   EXPECT_EQ(32U, name_offset);
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_32) {
+TEST_F(ElfInterfaceTest, init_section_headers32) {
   InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_64) {
+TEST_F(ElfInterfaceTest, init_section_headers64) {
   InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size_32) {
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) {
   InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size_64) {
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) {
   InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
 }
 
@@ -979,7 +961,7 @@
   shdr.sh_type = SHT_PROGBITS;
   shdr.sh_link = 2;
   shdr.sh_name = 0x400;
-  shdr.sh_addr = 0xa000;
+  shdr.sh_addr = 0x6000;
   shdr.sh_offset = 0xa000;
   shdr.sh_entsize = 0x100;
   shdr.sh_size = 0xf00;
@@ -989,10 +971,10 @@
   memset(&shdr, 0, sizeof(shdr));
   shdr.sh_type = SHT_NOTE;
   shdr.sh_name = 0x500;
-  shdr.sh_addr = 0xb000;
   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"));
@@ -1000,352 +982,29 @@
   memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
   memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
   EXPECT_EQ(0x6000U, elf->debug_frame_offset());
-  EXPECT_EQ(0, elf->debug_frame_section_bias());
   EXPECT_EQ(0x500U, elf->debug_frame_size());
-
   EXPECT_EQ(0x5000U, elf->gnu_debugdata_offset());
   EXPECT_EQ(0x800U, elf->gnu_debugdata_size());
-
   EXPECT_EQ(0x7000U, elf->eh_frame_offset());
-  EXPECT_EQ(0, elf->eh_frame_section_bias());
   EXPECT_EQ(0x800U, elf->eh_frame_size());
-
   EXPECT_EQ(0xa000U, elf->eh_frame_hdr_offset());
-  EXPECT_EQ(0, elf->eh_frame_hdr_section_bias());
   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_offsets_32) {
+TEST_F(ElfInterfaceTest, init_section_headers_offsets32) {
   InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_64) {
+TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
   InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
 
-template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
-void ElfInterfaceTest::InitSectionHeadersOffsetsEhFrameSectionBias(uint64_t addr, uint64_t offset,
-                                                                   int64_t expected_bias) {
-  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
-
-  uint64_t elf_offset = 0x2000;
-
-  Ehdr ehdr = {};
-  ehdr.e_shoff = elf_offset;
-  ehdr.e_shnum = 4;
-  ehdr.e_shentsize = sizeof(Shdr);
-  ehdr.e_shstrndx = 2;
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  elf_offset += ehdr.e_shentsize;
-
-  Shdr shdr = {};
-  shdr.sh_type = SHT_PROGBITS;
-  shdr.sh_link = 2;
-  shdr.sh_name = 0x200;
-  shdr.sh_addr = 0x8000;
-  shdr.sh_offset = 0x8000;
-  shdr.sh_entsize = 0x100;
-  shdr.sh_size = 0x800;
-  memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
-  elf_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(elf_offset, &shdr, sizeof(shdr));
-  elf_offset += ehdr.e_shentsize;
-
-  memset(&shdr, 0, sizeof(shdr));
-  shdr.sh_type = SHT_PROGBITS;
-  shdr.sh_link = 2;
-  shdr.sh_name = 0x100;
-  shdr.sh_addr = addr;
-  shdr.sh_offset = offset;
-  shdr.sh_entsize = 0x100;
-  shdr.sh_size = 0x500;
-  memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
-
-  memory_.SetMemory(0xf100, ".eh_frame", sizeof(".eh_frame"));
-  memory_.SetMemory(0xf200, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
-
-  int64_t load_bias = 0;
-  ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
-  EXPECT_EQ(offset, elf->eh_frame_offset());
-  EXPECT_EQ(expected_bias, elf->eh_frame_section_bias());
-  EXPECT_EQ(0x500U, elf->eh_frame_size());
-
-  EXPECT_EQ(0x8000U, elf->eh_frame_hdr_offset());
-  EXPECT_EQ(0, elf->eh_frame_hdr_section_bias());
-  EXPECT_EQ(0x800U, elf->eh_frame_hdr_size());
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_zero_32) {
-  InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x4000,
-                                                                                      0x4000, 0);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_zero_64) {
-  InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0x6000,
-                                                                                      0x6000, 0);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_positive_32) {
-  InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
-      0x5000, 0x4000, 0x1000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_positive_64) {
-  InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
-      0x6000, 0x4000, 0x2000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_negative_32) {
-  InitSectionHeadersOffsetsEhFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
-      0x3000, 0x4000, -0x1000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_section_bias_negative_64) {
-  InitSectionHeadersOffsetsEhFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
-      0x6000, 0x9000, -0x3000);
-}
-
-template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
-void ElfInterfaceTest::InitSectionHeadersOffsetsEhFrameHdrSectionBias(uint64_t addr,
-                                                                      uint64_t offset,
-                                                                      int64_t expected_bias) {
-  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
-
-  uint64_t elf_offset = 0x2000;
-
-  Ehdr ehdr = {};
-  ehdr.e_shoff = elf_offset;
-  ehdr.e_shnum = 4;
-  ehdr.e_shentsize = sizeof(Shdr);
-  ehdr.e_shstrndx = 2;
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  elf_offset += ehdr.e_shentsize;
-
-  Shdr shdr = {};
-  shdr.sh_type = SHT_PROGBITS;
-  shdr.sh_link = 2;
-  shdr.sh_name = 0x200;
-  shdr.sh_addr = addr;
-  shdr.sh_offset = offset;
-  shdr.sh_entsize = 0x100;
-  shdr.sh_size = 0x800;
-  memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
-  elf_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(elf_offset, &shdr, sizeof(shdr));
-  elf_offset += ehdr.e_shentsize;
-
-  memset(&shdr, 0, sizeof(shdr));
-  shdr.sh_type = SHT_PROGBITS;
-  shdr.sh_link = 2;
-  shdr.sh_name = 0x100;
-  shdr.sh_addr = 0x5000;
-  shdr.sh_offset = 0x5000;
-  shdr.sh_entsize = 0x100;
-  shdr.sh_size = 0x500;
-  memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
-
-  memory_.SetMemory(0xf100, ".eh_frame", sizeof(".eh_frame"));
-  memory_.SetMemory(0xf200, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
-
-  int64_t load_bias = 0;
-  ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
-  EXPECT_EQ(0x5000U, elf->eh_frame_offset());
-  EXPECT_EQ(0, elf->eh_frame_section_bias());
-  EXPECT_EQ(0x500U, elf->eh_frame_size());
-  EXPECT_EQ(offset, elf->eh_frame_hdr_offset());
-  EXPECT_EQ(expected_bias, elf->eh_frame_hdr_section_bias());
-  EXPECT_EQ(0x800U, elf->eh_frame_hdr_size());
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_zero_32) {
-  InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x9000,
-                                                                                         0x9000, 0);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_zero_64) {
-  InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0xa000,
-                                                                                         0xa000, 0);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_positive_32) {
-  InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
-      0x9000, 0x4000, 0x5000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_positive_64) {
-  InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
-      0x6000, 0x1000, 0x5000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_negative_32) {
-  InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
-      0x3000, 0x5000, -0x2000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_eh_frame_hdr_section_bias_negative_64) {
-  InitSectionHeadersOffsetsEhFrameHdrSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
-      0x5000, 0x9000, -0x4000);
-}
-
-template <typename Ehdr, typename Shdr, typename ElfInterfaceType>
-void ElfInterfaceTest::InitSectionHeadersOffsetsDebugFrameSectionBias(uint64_t addr,
-                                                                      uint64_t offset,
-                                                                      int64_t expected_bias) {
-  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
-
-  uint64_t elf_offset = 0x2000;
-
-  Ehdr ehdr = {};
-  ehdr.e_shoff = elf_offset;
-  ehdr.e_shnum = 3;
-  ehdr.e_shentsize = sizeof(Shdr);
-  ehdr.e_shstrndx = 2;
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  elf_offset += ehdr.e_shentsize;
-
-  Shdr shdr = {};
-  shdr.sh_type = SHT_PROGBITS;
-  shdr.sh_link = 2;
-  shdr.sh_name = 0x100;
-  shdr.sh_addr = addr;
-  shdr.sh_offset = offset;
-  shdr.sh_entsize = 0x100;
-  shdr.sh_size = 0x800;
-  memory_.SetMemory(elf_offset, &shdr, sizeof(shdr));
-  elf_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(elf_offset, &shdr, sizeof(shdr));
-
-  memory_.SetMemory(0xf100, ".debug_frame", sizeof(".debug_frame"));
-
-  int64_t load_bias = 0;
-  ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
-  EXPECT_EQ(offset, elf->debug_frame_offset());
-  EXPECT_EQ(expected_bias, elf->debug_frame_section_bias());
-  EXPECT_EQ(0x800U, elf->debug_frame_size());
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_zero_32) {
-  InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(0x5000,
-                                                                                         0x5000, 0);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_zero_64) {
-  InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(0xa000,
-                                                                                         0xa000, 0);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_positive_32) {
-  InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
-      0x5000, 0x2000, 0x3000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_positive_64) {
-  InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
-      0x7000, 0x1000, 0x6000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_negative_32) {
-  InitSectionHeadersOffsetsDebugFrameSectionBias<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>(
-      0x6000, 0x7000, -0x1000);
-}
-
-TEST_F(ElfInterfaceTest, init_section_headers_offsets_debug_frame_section_bias_negative_64) {
-  InitSectionHeadersOffsetsDebugFrameSectionBias<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>(
-      0x3000, 0x5000, -0x2000);
-}
-
-template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
-void ElfInterfaceTest::CheckGnuEhFrame(uint64_t addr, uint64_t offset, int64_t expected_bias) {
-  std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
-
-  Ehdr ehdr = {};
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 2;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  uint64_t phdr_offset = 0x100;
-
-  Phdr phdr = {};
-  phdr.p_type = PT_LOAD;
-  phdr.p_memsz = 0x10000;
-  phdr.p_flags = PF_R | PF_X;
-  phdr.p_align = 0x1000;
-  memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
-  phdr_offset += sizeof(phdr);
-
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_GNU_EH_FRAME;
-  phdr.p_vaddr = addr;
-  phdr.p_offset = offset;
-  memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
-
-  int64_t load_bias = 0;
-  ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
-  EXPECT_EQ(expected_bias, elf->eh_frame_hdr_section_bias());
-}
-
-TEST_F(ElfInterfaceTest, eh_frame_zero_section_bias_32) {
-  ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x4000, 0);
-}
-
-TEST_F(ElfInterfaceTest, eh_frame_zero_section_bias_64) {
-  ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x4000, 0);
-}
-
-TEST_F(ElfInterfaceTest, eh_frame_positive_section_bias_32) {
-  ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x1000, 0x3000);
-}
-
-TEST_F(ElfInterfaceTest, eh_frame_positive_section_bias_64) {
-  ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x1000, 0x3000);
-}
-
-TEST_F(ElfInterfaceTest, eh_frame_negative_section_bias_32) {
-  ElfInterfaceTest::CheckGnuEhFrame<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x4000, 0x5000,
-                                                                            -0x1000);
-}
-
-TEST_F(ElfInterfaceTest, eh_frame_negative_section_bias_64) {
-  ElfInterfaceTest::CheckGnuEhFrame<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x4000, 0x5000,
-                                                                            -0x1000);
-}
-
 TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
   std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
 
@@ -1363,9 +1022,9 @@
   phdr.p_align = 0x1000;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0, load_bias);
+  EXPECT_EQ(0U, load_bias);
   EXPECT_TRUE(elf->IsValidPc(0));
   EXPECT_TRUE(elf->IsValidPc(0x5000));
   EXPECT_TRUE(elf->IsValidPc(0xffff));
@@ -1389,9 +1048,9 @@
   phdr.p_align = 0x1000;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000, load_bias);
+  EXPECT_EQ(0x2000U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x1000));
   EXPECT_FALSE(elf->IsValidPc(0x1fff));
@@ -1446,10 +1105,10 @@
   memory_.SetData32(0x708, 0x2100);
   memory_.SetData32(0x70c, 0x200);
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders();
-  EXPECT_EQ(0, load_bias);
+  elf->InitHeaders(0);
+  EXPECT_EQ(0U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x20ff));
   EXPECT_TRUE(elf->IsValidPc(0x2100));
@@ -1503,10 +1162,10 @@
   memory_.SetData32(0x708, 0x20f8);
   memory_.SetData32(0x70c, 0x200);
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders();
-  EXPECT_EQ(0, load_bias);
+  elf->InitHeaders(0);
+  EXPECT_EQ(0U, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x27ff));
   EXPECT_TRUE(elf->IsValidPc(0x2800));
@@ -1542,6 +1201,7 @@
   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;
@@ -1558,23 +1218,16 @@
   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));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   ASSERT_EQ("BUILDID", elf->GetBuildID());
 }
 
-TEST_F(ElfInterfaceTest, build_id_32) {
-  BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_64) {
-  BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
 void ElfInterfaceTest::BuildIDTwoNotes() {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1613,6 +1266,7 @@
   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;
@@ -1629,23 +1283,16 @@
   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));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   ASSERT_EQ("BUILDID", elf->GetBuildID());
 }
 
-TEST_F(ElfInterfaceTest, build_id_two_notes_32) {
-  BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_two_notes_64) {
-  BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
 void ElfInterfaceTest::BuildIDSectionTooSmallForName () {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1673,6 +1320,7 @@
   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;
@@ -1689,23 +1337,16 @@
   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));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   ASSERT_EQ("", elf->GetBuildID());
 }
 
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name_32) {
-  BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name_64) {
-  BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
 void ElfInterfaceTest::BuildIDSectionTooSmallForDesc () {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1733,6 +1374,7 @@
   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;
@@ -1749,23 +1391,16 @@
   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));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   ASSERT_EQ("", elf->GetBuildID());
 }
 
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc_32) {
-  BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc_64) {
-  BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
 template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
 void ElfInterfaceTest::BuildIDSectionTooSmallForHeader () {
   std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
@@ -1793,6 +1428,7 @@
   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;
@@ -1809,139 +1445,54 @@
   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));
 
-  int64_t load_bias = 0;
+  uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   ASSERT_EQ("", elf->GetBuildID());
 }
 
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header_32) {
+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_header_64) {
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header64) {
   BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
 }
 
-template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
-void ElfInterfaceTest::CheckLoadBiasInFirstPhdr(int64_t load_bias) {
-  Ehdr ehdr = {};
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 2;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  Phdr phdr = {};
-  phdr.p_type = PT_LOAD;
-  phdr.p_offset = 0;
-  phdr.p_vaddr = load_bias;
-  phdr.p_memsz = 0x10000;
-  phdr.p_flags = PF_R | PF_X;
-  phdr.p_align = 0x1000;
-  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
-
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_LOAD;
-  phdr.p_offset = 0x1000;
-  phdr.p_memsz = 0x2000;
-  phdr.p_flags = PF_R | PF_X;
-  phdr.p_align = 0x1000;
-  memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
-
-  int64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
-  ASSERT_EQ(load_bias, static_load_bias);
-
-  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
-  int64_t init_load_bias = 0;
-  ASSERT_TRUE(elf->Init(&init_load_bias));
-  ASSERT_EQ(init_load_bias, static_load_bias);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_zero_32) {
-  CheckLoadBiasInFirstPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_zero_64) {
-  CheckLoadBiasInFirstPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_non_zero_32) {
-  CheckLoadBiasInFirstPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_non_zero_64) {
-  CheckLoadBiasInFirstPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000);
-}
-
-template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
-void ElfInterfaceTest::CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr,
-                                                    int64_t load_bias) {
-  Ehdr ehdr = {};
-  ehdr.e_phoff = 0x100;
-  ehdr.e_phnum = 3;
-  ehdr.e_phentsize = sizeof(Phdr);
-  memory_.SetMemory(0, &ehdr, sizeof(ehdr));
-
-  Phdr phdr = {};
-  phdr.p_type = PT_LOAD;
-  phdr.p_memsz = 0x10000;
-  phdr.p_flags = PF_R;
-  phdr.p_align = 0x1000;
-  memory_.SetMemory(0x100, &phdr, sizeof(phdr));
-
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_LOAD;
-  phdr.p_offset = offset;
-  phdr.p_vaddr = vaddr;
-  phdr.p_memsz = 0x2000;
-  phdr.p_flags = PF_R | PF_X;
-  phdr.p_align = 0x1000;
-  memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
-
-  // Second executable load should be ignored for load bias computation.
-  memset(&phdr, 0, sizeof(phdr));
-  phdr.p_type = PT_LOAD;
-  phdr.p_offset = 0x1234;
-  phdr.p_vaddr = 0x2000;
-  phdr.p_memsz = 0x2000;
-  phdr.p_flags = PF_R | PF_X;
-  phdr.p_align = 0x1000;
-  memory_.SetMemory(0x200 + sizeof(phdr), &phdr, sizeof(phdr));
-
-  int64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
-  ASSERT_EQ(load_bias, static_load_bias);
-
-  std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
-  int64_t init_load_bias = 0;
-  ASSERT_TRUE(elf->Init(&init_load_bias));
-  ASSERT_EQ(init_load_bias, static_load_bias);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_32) {
-  CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x1000, 0);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_64) {
-  CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x1000, 0);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_exec_positive_32) {
-  CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x4000, 0x3000);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_exec_positive_64) {
-  CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x4000, 0x3000);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_exec_negative_32) {
-  CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x5000, 0x1000, -0x4000);
-}
-
-TEST_F(ElfInterfaceTest, get_load_bias_exec_negative_64) {
-  CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, -0x4000);
-}
-
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index e6728a0..c432d6d 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -310,15 +310,15 @@
   ElfInterfaceMock(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterfaceMock() = default;
 
-  bool Init(int64_t*) override { return false; }
-  void InitHeaders() override {}
+  bool Init(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_METHOD(bool, Step, (uint64_t, Regs*, Memory*, bool*), (override));
-  MOCK_METHOD(bool, GetGlobalVariable, (const std::string&, uint64_t*), (override));
-  MOCK_METHOD(bool, IsValidPc, (uint64_t), (override));
+  MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
+  MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
+  MOCK_METHOD1(IsValidPc, bool(uint64_t));
 
   void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
   void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
diff --git a/libunwindstack/tests/IsolatedSettings.cpp b/libunwindstack/tests/IsolatedSettings.cpp
deleted file mode 100644
index dbd8bd6..0000000
--- a/libunwindstack/tests/IsolatedSettings.cpp
+++ /dev/null
@@ -1,26 +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 <stdint.h>
-#include <stdio.h>
-
-extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
-  static const char* initial_args[2] = {"--slow_threshold_ms=90000",
-                                        "--deadline_threshold_ms=120000"};
-  *args = initial_args;
-  *num_args = 2;
-  return true;
-}
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
index 9936f7a..56a18cd 100644
--- a/libunwindstack/tests/LocalUnwinderTest.cpp
+++ b/libunwindstack/tests/LocalUnwinderTest.cpp
@@ -170,10 +170,10 @@
 
   std::string testlib(testing::internal::GetArgvs()[0]);
   auto const value = testlib.find_last_of('/');
-  if (value != std::string::npos) {
-    testlib = testlib.substr(0, value + 1);
+  if (value == std::string::npos) {
+    testlib = "../";
   } else {
-    testlib = "";
+    testlib = testlib.substr(0, value + 1) + "../";
   }
   testlib += "libunwindstack_local.so";
 
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
deleted file mode 100644
index b816b9a..0000000
--- a/libunwindstack/tests/LocalUpdatableMapsTest.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * 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 <stdint.h>
-#include <sys/mman.h>
-
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include <android-base/file.h>
-#include <unwindstack/Maps.h>
-
-namespace unwindstack {
-
-class TestUpdatableMaps : public LocalUpdatableMaps {
- public:
-  TestUpdatableMaps() : LocalUpdatableMaps() {}
-  virtual ~TestUpdatableMaps() = default;
-
-  const std::string GetMapsFile() const override { return maps_file_; }
-
-  void TestSetMapsFile(const std::string& maps_file) { maps_file_ = maps_file; }
-
-  const std::vector<std::unique_ptr<MapInfo>>& TestGetSavedMaps() { return saved_maps_; }
-
- private:
-  std::string maps_file_;
-};
-
-class LocalUpdatableMapsTest : public ::testing::Test {
- protected:
-  static const std::string GetDefaultMapString() {
-    return "3000-4000 r-xp 00000 00:00 0\n8000-9000 r-xp 00000 00:00 0\n";
-  }
-
-  void SetUp() override {
-    TemporaryFile tf;
-    ASSERT_TRUE(android::base::WriteStringToFile(GetDefaultMapString(), tf.path));
-
-    maps_.TestSetMapsFile(tf.path);
-    ASSERT_TRUE(maps_.Parse());
-    ASSERT_EQ(2U, maps_.Total());
-
-    MapInfo* map_info = maps_.Get(0);
-    ASSERT_TRUE(map_info != nullptr);
-    EXPECT_EQ(0x3000U, map_info->start);
-    EXPECT_EQ(0x4000U, map_info->end);
-    EXPECT_EQ(0U, map_info->offset);
-    EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-    EXPECT_TRUE(map_info->name.empty());
-
-    map_info = maps_.Get(1);
-    ASSERT_TRUE(map_info != nullptr);
-    EXPECT_EQ(0x8000U, map_info->start);
-    EXPECT_EQ(0x9000U, map_info->end);
-    EXPECT_EQ(0U, map_info->offset);
-    EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-    EXPECT_TRUE(map_info->name.empty());
-  }
-
-  TestUpdatableMaps maps_;
-};
-
-TEST_F(LocalUpdatableMapsTest, same_map) {
-  TemporaryFile tf;
-  ASSERT_TRUE(android::base::WriteStringToFile(GetDefaultMapString(), tf.path));
-
-  maps_.TestSetMapsFile(tf.path);
-  ASSERT_TRUE(maps_.Reparse());
-  ASSERT_EQ(2U, maps_.Total());
-  EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
-
-  MapInfo* map_info = maps_.Get(0);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start);
-  EXPECT_EQ(0x4000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  map_info = maps_.Get(1);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x8000U, map_info->start);
-  EXPECT_EQ(0x9000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-}
-
-TEST_F(LocalUpdatableMapsTest, same_map_new_perms) {
-  TemporaryFile tf;
-  ASSERT_TRUE(
-      android::base::WriteStringToFile("3000-4000 rwxp 00000 00:00 0\n"
-                                       "8000-9000 r-xp 00000 00:00 0\n",
-                                       tf.path));
-
-  maps_.TestSetMapsFile(tf.path);
-  ASSERT_TRUE(maps_.Reparse());
-  ASSERT_EQ(2U, maps_.Total());
-
-  MapInfo* map_info = maps_.Get(0);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start);
-  EXPECT_EQ(0x4000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  map_info = maps_.Get(1);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x8000U, map_info->start);
-  EXPECT_EQ(0x9000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  auto& saved_maps = maps_.TestGetSavedMaps();
-  ASSERT_EQ(1U, saved_maps.size());
-  map_info = saved_maps[0].get();
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start);
-  EXPECT_EQ(0x4000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-}
-
-TEST_F(LocalUpdatableMapsTest, same_map_new_name) {
-  TemporaryFile tf;
-  ASSERT_TRUE(
-      android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
-                                       "8000-9000 r-xp 00000 00:00 0\n",
-                                       tf.path));
-
-  maps_.TestSetMapsFile(tf.path);
-  ASSERT_TRUE(maps_.Reparse());
-  ASSERT_EQ(2U, maps_.Total());
-
-  MapInfo* map_info = maps_.Get(0);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start);
-  EXPECT_EQ(0x4000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_EQ("/fake/lib.so", map_info->name);
-
-  map_info = maps_.Get(1);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x8000U, map_info->start);
-  EXPECT_EQ(0x9000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  auto& saved_maps = maps_.TestGetSavedMaps();
-  ASSERT_EQ(1U, saved_maps.size());
-  map_info = saved_maps[0].get();
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start);
-  EXPECT_EQ(0x4000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-}
-
-TEST_F(LocalUpdatableMapsTest, only_add_maps) {
-  TemporaryFile tf;
-  ASSERT_TRUE(
-      android::base::WriteStringToFile("1000-2000 r-xp 00000 00:00 0\n"
-                                       "3000-4000 r-xp 00000 00:00 0\n"
-                                       "8000-9000 r-xp 00000 00:00 0\n"
-                                       "a000-f000 r-xp 00000 00:00 0\n",
-                                       tf.path));
-
-  maps_.TestSetMapsFile(tf.path);
-  ASSERT_TRUE(maps_.Reparse());
-  ASSERT_EQ(4U, maps_.Total());
-  EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
-
-  MapInfo* map_info = maps_.Get(0);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x1000U, map_info->start);
-  EXPECT_EQ(0x2000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  map_info = maps_.Get(1);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start);
-  EXPECT_EQ(0x4000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  map_info = maps_.Get(2);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x8000U, map_info->start);
-  EXPECT_EQ(0x9000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  map_info = maps_.Get(3);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0xa000U, map_info->start);
-  EXPECT_EQ(0xf000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-}
-
-TEST_F(LocalUpdatableMapsTest, all_new_maps) {
-  TemporaryFile tf;
-  ASSERT_TRUE(
-      android::base::WriteStringToFile("1000-2000 r-xp 00000 00:00 0\n"
-                                       "a000-f000 r-xp 00000 00:00 0\n",
-                                       tf.path));
-
-  maps_.TestSetMapsFile(tf.path);
-  ASSERT_TRUE(maps_.Reparse());
-  ASSERT_EQ(2U, maps_.Total());
-
-  MapInfo* map_info = maps_.Get(0);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x1000U, map_info->start);
-  EXPECT_EQ(0x2000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  map_info = maps_.Get(1);
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0xa000U, map_info->start);
-  EXPECT_EQ(0xf000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  auto& saved_maps = maps_.TestGetSavedMaps();
-  ASSERT_EQ(2U, saved_maps.size());
-  map_info = saved_maps[0].get();
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x3000U, map_info->start);
-  EXPECT_EQ(0x4000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-
-  map_info = saved_maps[1].get();
-  ASSERT_TRUE(map_info != nullptr);
-  EXPECT_EQ(0x8000U, map_info->start);
-  EXPECT_EQ(0x9000U, map_info->end);
-  EXPECT_EQ(0U, map_info->offset);
-  EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags);
-  EXPECT_TRUE(map_info->name.empty());
-}
-
-}  // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 6c1cfa2..6be8bdc 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -58,7 +58,7 @@
     ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
   }
 
-  void SetUp() override {
+  static void SetUpTestCase() {
     std::vector<uint8_t> buffer(12288, 0);
     memcpy(buffer.data(), ELFMAG, SELFMAG);
     buffer[EI_CLASS] = ELFCLASS32;
@@ -72,7 +72,9 @@
 
     InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
     InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
+  }
 
+  void SetUp() override {
     memory_ = new MemoryFake;
     process_memory_.reset(memory_);
   }
@@ -80,13 +82,17 @@
   MemoryFake* memory_;
   std::shared_ptr<Memory> process_memory_;
 
-  TemporaryFile elf_;
+  static TemporaryFile elf_;
 
-  TemporaryFile elf_at_1000_;
+  static TemporaryFile elf_at_1000_;
 
-  TemporaryFile elf32_at_map_;
-  TemporaryFile elf64_at_map_;
+  static TemporaryFile elf32_at_map_;
+  static TemporaryFile elf64_at_map_;
 };
+TemporaryFile MapInfoCreateMemoryTest::elf_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_1000_;
+TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
+TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
   MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index da3dbf2..f5ac6cb 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -84,7 +84,7 @@
   elf_->FakeSetLoadBias(0);
   EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
 
-  map_info_->load_bias = INT64_MAX;
+  map_info_->load_bias = static_cast<uint64_t>(-1);
   elf_->FakeSetLoadBias(0x1000);
   EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
 }
@@ -141,7 +141,6 @@
   phdr.p_type = PT_NULL;
   memory->SetMemory(offset + 0x5000, &phdr, sizeof(phdr));
   phdr.p_type = PT_LOAD;
-  phdr.p_flags = PF_X;
   phdr.p_offset = 0;
   phdr.p_vaddr = 0xe000;
   memory->SetMemory(offset + 0x5000 + sizeof(phdr), &phdr, sizeof(phdr));
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
index ef76b1b..e2cbb98 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -35,7 +35,7 @@
   EXPECT_EQ(3UL, map_info.offset);
   EXPECT_EQ(4UL, map_info.flags);
   EXPECT_EQ("map", map_info.name);
-  EXPECT_EQ(INT64_MAX, map_info.load_bias);
+  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);
 }
@@ -51,7 +51,7 @@
   EXPECT_EQ(3UL, map_info.offset);
   EXPECT_EQ(4UL, map_info.flags);
   EXPECT_EQ("string_map", map_info.name);
-  EXPECT_EQ(INT64_MAX, map_info.load_bias);
+  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);
 }
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
index a6c12aa..28e0e76 100644
--- a/libunwindstack/tests/MemoryBufferTest.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -18,8 +18,9 @@
 
 #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
index 3bd3e4d..a3def20 100644
--- a/libunwindstack/tests/MemoryCacheTest.cpp
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -20,7 +20,8 @@
 
 #include <gtest/gtest.h>
 
-#include "MemoryCache.h"
+#include <unwindstack/Memory.h>
+
 #include "MemoryFake.h"
 
 namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index 4124a49..d7d1ace 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 "MemoryFileAtOffset.h"
+#include <unwindstack/Memory.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index c9e5dc0..5a389d0 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -22,7 +22,7 @@
 
 #include <gtest/gtest.h>
 
-#include "MemoryLocal.h"
+#include <unwindstack/Memory.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryOfflineBufferTest.cpp b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
index 9531708..f022884 100644
--- a/libunwindstack/tests/MemoryOfflineBufferTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineBufferTest.cpp
@@ -18,8 +18,9 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Memory.h>
+
 #include "LogFake.h"
-#include "MemoryOfflineBuffer.h"
 
 namespace unwindstack {
 
@@ -30,7 +31,7 @@
     memory_.reset(new MemoryOfflineBuffer(buffer_.data(), kStart, kEnd));
   }
 
-  static void SetUpTestSuite() {
+  static void SetUpTestCase() {
     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 d0c441b..ab9aa9d 100644
--- a/libunwindstack/tests/MemoryOfflineTest.cpp
+++ b/libunwindstack/tests/MemoryOfflineTest.cpp
@@ -19,8 +19,7 @@
 #include <gtest/gtest.h>
 
 #include <android-base/file.h>
-
-#include "MemoryOffline.h"
+#include <unwindstack/Memory.h>
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index 2d4f141..2bac95b 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -21,8 +21,9 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Memory.h>
+
 #include "MemoryFake.h"
-#include "MemoryRange.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
index e4e9fc4..d24fcd2 100644
--- a/libunwindstack/tests/MemoryRangesTest.cpp
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -20,8 +20,9 @@
 
 #include <gtest/gtest.h>
 
+#include <unwindstack/Memory.h>
+
 #include "MemoryFake.h"
-#include "MemoryRange.h"
 
 namespace unwindstack {
 
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index c90dedc..fb56e8a 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 "MemoryRemote.h"
+#include <unwindstack/Memory.h>
 
 #include "MemoryFake.h"
 #include "TestUtils.h"
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 7e36953..9a27dbd 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_SUITE(RegsIterateTest, RegTypes);
+TYPED_TEST_CASE(RegsIterateTest, RegTypes);
 
 TYPED_TEST(RegsIterateTest, iterate) {
   std::vector<Register> expected = ExpectedRegisters<TypeParam>();
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index ae3c349..b40a253 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -55,7 +55,7 @@
 
   MemoryFake memory_;
 };
-TYPED_TEST_SUITE_P(SymbolsTest);
+TYPED_TEST_CASE_P(SymbolsTest);
 
 TYPED_TEST_P(SymbolsTest, function_bounds_check) {
   Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
@@ -362,11 +362,11 @@
   EXPECT_EQ(4U, offset);
 }
 
-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);
+REGISTER_TYPED_TEST_CASE_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_SUITE_P(, SymbolsTest, SymbolsTestTypes);
+INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
 
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 0d58c09..6c64c40 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -35,6 +35,7 @@
 #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,7 +43,6 @@
 #include <unwindstack/Unwinder.h>
 
 #include "ElfTestUtils.h"
-#include "MemoryOffline.h"
 #include "TestUtils.h"
 
 namespace unwindstack {
@@ -62,7 +62,7 @@
     free(cwd_);
   }
 
-  void Init(const char* file_dir, ArchEnum arch, bool add_stack = true) {
+  void Init(const char* file_dir, ArchEnum arch) {
     dir_ = TestGetFileDirectory() + "offline/" + file_dir;
 
     std::string data;
@@ -71,25 +71,23 @@
     maps_.reset(new BufferMaps(data.c_str()));
     ASSERT_TRUE(maps_->Parse());
 
-    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());
+    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;
         }
-        process_memory_.reset(stack_memory.release());
+        AddMemory(stack_name, stack_memory.get());
       }
+      process_memory_.reset(stack_memory.release());
     }
 
     switch (arch) {
@@ -1444,189 +1442,4 @@
   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);
-}
-
-TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
-  ASSERT_NO_FATAL_FAILURE(Init("load_bias_ro_rx_x86_64/", ARCH_X86_64));
-
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.Unwind();
-
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00000000000e9dd4  libc.so (__write+20)\n"
-      "  #01 pc 000000000007ab9c  libc.so (_IO_file_write+44)\n"
-      "  #02 pc 0000000000079f3e  libc.so\n"
-      "  #03 pc 000000000007bce8  libc.so (_IO_do_write+24)\n"
-      "  #04 pc 000000000007b26e  libc.so (_IO_file_xsputn+270)\n"
-      "  #05 pc 000000000004f7f9  libc.so (_IO_vfprintf+1945)\n"
-      "  #06 pc 0000000000057cb5  libc.so (_IO_printf+165)\n"
-      "  #07 pc 0000000000ed1796  perfetto_unittests "
-      "(testing::internal::PrettyUnitTestResultPrinter::OnTestIterationStart(testing::UnitTest "
-      "const&, int)+374)\n"
-      "  #08 pc 0000000000ed30fd  perfetto_unittests "
-      "(testing::internal::TestEventRepeater::OnTestIterationStart(testing::UnitTest const&, "
-      "int)+125)\n"
-      "  #09 pc 0000000000ed5e25  perfetto_unittests "
-      "(testing::internal::UnitTestImpl::RunAllTests()+581)\n"
-      "  #10 pc 0000000000ef63f3  perfetto_unittests "
-      "(bool "
-      "testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
-      "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
-      "const*)+131)\n"
-      "  #11 pc 0000000000ee2a21  perfetto_unittests "
-      "(bool "
-      "testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
-      "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
-      "const*)+113)\n"
-      "  #12 pc 0000000000ed5bb9  perfetto_unittests (testing::UnitTest::Run()+185)\n"
-      "  #13 pc 0000000000e900f0  perfetto_unittests (RUN_ALL_TESTS()+16)\n"
-      "  #14 pc 0000000000e900d8  perfetto_unittests (main+56)\n"
-      "  #15 pc 000000000002352a  libc.so (__libc_start_main+234)\n"
-      "  #16 pc 0000000000919029  perfetto_unittests (_start+41)\n",
-      frame_info);
-
-  EXPECT_EQ(0x7f9326a57dd4ULL, unwinder.frames()[0].pc);
-  EXPECT_EQ(0x7ffd224153c8ULL, unwinder.frames()[0].sp);
-  EXPECT_EQ(0x7f93269e8b9cULL, unwinder.frames()[1].pc);
-  EXPECT_EQ(0x7ffd224153d0ULL, unwinder.frames()[1].sp);
-  EXPECT_EQ(0x7f93269e7f3eULL, unwinder.frames()[2].pc);
-  EXPECT_EQ(0x7ffd22415400ULL, unwinder.frames()[2].sp);
-  EXPECT_EQ(0x7f93269e9ce8ULL, unwinder.frames()[3].pc);
-  EXPECT_EQ(0x7ffd22415440ULL, unwinder.frames()[3].sp);
-  EXPECT_EQ(0x7f93269e926eULL, unwinder.frames()[4].pc);
-  EXPECT_EQ(0x7ffd22415450ULL, unwinder.frames()[4].sp);
-  EXPECT_EQ(0x7f93269bd7f9ULL, unwinder.frames()[5].pc);
-  EXPECT_EQ(0x7ffd22415490ULL, unwinder.frames()[5].sp);
-  EXPECT_EQ(0x7f93269c5cb5ULL, unwinder.frames()[6].pc);
-  EXPECT_EQ(0x7ffd22415a10ULL, unwinder.frames()[6].sp);
-  EXPECT_EQ(0xed1796ULL, unwinder.frames()[7].pc);
-  EXPECT_EQ(0x7ffd22415af0ULL, unwinder.frames()[7].sp);
-  EXPECT_EQ(0xed30fdULL, unwinder.frames()[8].pc);
-  EXPECT_EQ(0x7ffd22415b70ULL, unwinder.frames()[8].sp);
-  EXPECT_EQ(0xed5e25ULL, unwinder.frames()[9].pc);
-  EXPECT_EQ(0x7ffd22415bb0ULL, unwinder.frames()[9].sp);
-  EXPECT_EQ(0xef63f3ULL, unwinder.frames()[10].pc);
-  EXPECT_EQ(0x7ffd22415c60ULL, unwinder.frames()[10].sp);
-  EXPECT_EQ(0xee2a21ULL, unwinder.frames()[11].pc);
-  EXPECT_EQ(0x7ffd22415cc0ULL, unwinder.frames()[11].sp);
-  EXPECT_EQ(0xed5bb9ULL, unwinder.frames()[12].pc);
-  EXPECT_EQ(0x7ffd22415d40ULL, unwinder.frames()[12].sp);
-  EXPECT_EQ(0xe900f0ULL, unwinder.frames()[13].pc);
-  EXPECT_EQ(0x7ffd22415d90ULL, unwinder.frames()[13].sp);
-  EXPECT_EQ(0xe900d8ULL, unwinder.frames()[14].pc);
-  EXPECT_EQ(0x7ffd22415da0ULL, unwinder.frames()[14].sp);
-  EXPECT_EQ(0x7f932699152aULL, unwinder.frames()[15].pc);
-  EXPECT_EQ(0x7ffd22415dd0ULL, unwinder.frames()[15].sp);
-  EXPECT_EQ(0x919029ULL, unwinder.frames()[16].pc);
-  EXPECT_EQ(0x7ffd22415e90ULL, unwinder.frames()[16].sp);
-}
-
-TEST_F(UnwindOfflineTest, load_bias_different_section_bias_arm64) {
-  ASSERT_NO_FATAL_FAILURE(Init("load_bias_different_section_bias_arm64/", ARCH_ARM64));
-
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.Unwind();
-
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(12U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc 00000000000d59bc  linker64 (__dl_syscall+28)\n"
-      "  #01 pc 00000000000554e8  linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1148)\n"
-      "  #02 pc 00000000000008c0  vdso (__kernel_rt_sigreturn)\n"
-      "  #03 pc 000000000007f3e8  libc.so (abort+168)\n"
-      "  #04 pc 00000000000459fc  test (std::__ndk1::__throw_bad_cast()+4)\n"
-      "  #05 pc 0000000000056d80  test (testing::Test::Run()+88)\n"
-      "  #06 pc 000000000005724c  test (testing::TestInfo::Run()+112)\n"
-      "  #07 pc 0000000000057558  test (testing::TestSuite::Run()+116)\n"
-      "  #08 pc 000000000005bffc  test (testing::internal::UnitTestImpl::RunAllTests()+464)\n"
-      "  #09 pc 000000000005bd9c  test (testing::UnitTest::Run()+116)\n"
-      "  #10 pc 00000000000464e4  test (main+144)\n"
-      "  #11 pc 000000000007aa34  libc.so (__libc_init+108)\n",
-      frame_info);
-
-  EXPECT_EQ(0x7112cb99bcULL, unwinder.frames()[0].pc);
-  EXPECT_EQ(0x7112bdbbf0ULL, unwinder.frames()[0].sp);
-  EXPECT_EQ(0x7112c394e8ULL, unwinder.frames()[1].pc);
-  EXPECT_EQ(0x7112bdbbf0ULL, unwinder.frames()[1].sp);
-  EXPECT_EQ(0x7112be28c0ULL, unwinder.frames()[2].pc);
-  EXPECT_EQ(0x7112bdbda0ULL, unwinder.frames()[2].sp);
-  EXPECT_EQ(0x71115ab3e8ULL, unwinder.frames()[3].pc);
-  EXPECT_EQ(0x7fdd4a3f00ULL, unwinder.frames()[3].sp);
-  EXPECT_EQ(0x5f739dc9fcULL, unwinder.frames()[4].pc);
-  EXPECT_EQ(0x7fdd4a3fe0ULL, unwinder.frames()[4].sp);
-  EXPECT_EQ(0x5f739edd80ULL, unwinder.frames()[5].pc);
-  EXPECT_EQ(0x7fdd4a3ff0ULL, unwinder.frames()[5].sp);
-  EXPECT_EQ(0x5f739ee24cULL, unwinder.frames()[6].pc);
-  EXPECT_EQ(0x7fdd4a4010ULL, unwinder.frames()[6].sp);
-  EXPECT_EQ(0x5f739ee558ULL, unwinder.frames()[7].pc);
-  EXPECT_EQ(0x7fdd4a4040ULL, unwinder.frames()[7].sp);
-  EXPECT_EQ(0x5f739f2ffcULL, unwinder.frames()[8].pc);
-  EXPECT_EQ(0x7fdd4a4070ULL, unwinder.frames()[8].sp);
-  EXPECT_EQ(0x5f739f2d9cULL, unwinder.frames()[9].pc);
-  EXPECT_EQ(0x7fdd4a4100ULL, unwinder.frames()[9].sp);
-  EXPECT_EQ(0x5f739dd4e4ULL, unwinder.frames()[10].pc);
-  EXPECT_EQ(0x7fdd4a4130ULL, unwinder.frames()[10].sp);
-  EXPECT_EQ(0x71115a6a34ULL, unwinder.frames()[11].pc);
-  EXPECT_EQ(0x7fdd4a4170ULL, unwinder.frames()[11].sp);
-}
-
-TEST_F(UnwindOfflineTest, eh_frame_bias_x86) {
-  ASSERT_NO_FATAL_FAILURE(Init("eh_frame_bias_x86/", ARCH_X86));
-
-  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
-  unwinder.Unwind();
-
-  std::string frame_info(DumpFrames(unwinder));
-  ASSERT_EQ(11U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
-  EXPECT_EQ(
-      "  #00 pc ffffe430  vdso.so (__kernel_vsyscall+16)\n"
-      "  #01 pc 00082a4b  libc.so (__epoll_pwait+43)\n"
-      "  #02 pc 000303a3  libc.so (epoll_pwait+115)\n"
-      "  #03 pc 000303ed  libc.so (epoll_wait+45)\n"
-      "  #04 pc 00010ea2  tombstoned (epoll_dispatch+226)\n"
-      "  #05 pc 0000c5e7  tombstoned (event_base_loop+1095)\n"
-      "  #06 pc 0000c193  tombstoned (event_base_dispatch+35)\n"
-      "  #07 pc 00005c77  tombstoned (main+884)\n"
-      "  #08 pc 00015f66  libc.so (__libc_init+102)\n"
-      "  #09 pc 0000360e  tombstoned (_start+98)\n"
-      "  #10 pc 00000001  <unknown>\n",
-      frame_info);
-
-  EXPECT_EQ(0xffffe430ULL, unwinder.frames()[0].pc);
-  EXPECT_EQ(0xfffe1a30ULL, unwinder.frames()[0].sp);
-  EXPECT_EQ(0xeb585a4bULL, unwinder.frames()[1].pc);
-  EXPECT_EQ(0xfffe1a40ULL, unwinder.frames()[1].sp);
-  EXPECT_EQ(0xeb5333a3ULL, unwinder.frames()[2].pc);
-  EXPECT_EQ(0xfffe1a60ULL, unwinder.frames()[2].sp);
-  EXPECT_EQ(0xeb5333edULL, unwinder.frames()[3].pc);
-  EXPECT_EQ(0xfffe1ab0ULL, unwinder.frames()[3].sp);
-  EXPECT_EQ(0xeb841ea2ULL, unwinder.frames()[4].pc);
-  EXPECT_EQ(0xfffe1ae0ULL, unwinder.frames()[4].sp);
-  EXPECT_EQ(0xeb83d5e7ULL, unwinder.frames()[5].pc);
-  EXPECT_EQ(0xfffe1b30ULL, unwinder.frames()[5].sp);
-  EXPECT_EQ(0xeb83d193ULL, unwinder.frames()[6].pc);
-  EXPECT_EQ(0xfffe1bd0ULL, unwinder.frames()[6].sp);
-  EXPECT_EQ(0xeb836c77ULL, unwinder.frames()[7].pc);
-  EXPECT_EQ(0xfffe1c00ULL, unwinder.frames()[7].sp);
-  EXPECT_EQ(0xeb518f66ULL, unwinder.frames()[8].pc);
-  EXPECT_EQ(0xfffe1d00ULL, unwinder.frames()[8].sp);
-  EXPECT_EQ(0xeb83460eULL, unwinder.frames()[9].pc);
-  EXPECT_EQ(0xfffe1d40ULL, unwinder.frames()[9].sp);
-  EXPECT_EQ(0x00000001ULL, unwinder.frames()[10].pc);
-  EXPECT_EQ(0xfffe1d74ULL, unwinder.frames()[10].sp);
-}
-
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index f76a101..4e38015 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -35,11 +35,11 @@
 #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 {
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index ef1950c..30e57a1 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -54,7 +54,7 @@
     }
   }
 
-  static void SetUpTestSuite() {
+  static void SetUpTestCase() {
     maps_.reset(new Maps);
 
     ElfFake* elf = new ElfFake(new MemoryFake);
@@ -132,10 +132,6 @@
     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);
   }
 
@@ -1019,50 +1015,6 @@
   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);
-  EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
-}
-
 TEST_F(UnwinderTest, dex_pc_not_in_map) {
   ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
   regs_.set_pc(0x1000);
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
deleted file mode 100644
index 6a3e91a..0000000
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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 _GNU_SOURCE 1
-#include <stdint.h>
-#include <string.h>
-
-#include <string>
-
-#if defined(__BIONIC__)
-
-#include <gtest/gtest.h>
-
-#include <unwindstack/DwarfSection.h>
-#include <unwindstack/Elf.h>
-#include <unwindstack/ElfInterface.h>
-#include <unwindstack/Regs.h>
-#include <unwindstack/RegsGetLocal.h>
-#include <unwindstack/Unwinder.h>
-
-// This test is specific to bionic to verify that __libc_init is
-// properly setting the return address to undefined so that the
-// unwind properly terminates.
-
-namespace unwindstack {
-
-static std::string DumpFrames(const UnwinderFromPid& unwinder) {
-  std::string unwind;
-  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
-    unwind += unwinder.FormatFrame(i) + '\n';
-  }
-  return unwind;
-}
-
-static DwarfLocationEnum GetReturnAddressLocation(uint64_t rel_pc, DwarfSection* section) {
-  if (section == nullptr) {
-    return DWARF_LOCATION_INVALID;
-  }
-
-  const DwarfFde* fde = section->GetFdeFromPc(rel_pc);
-  if (fde == nullptr || fde->cie == nullptr) {
-    return DWARF_LOCATION_INVALID;
-  }
-  dwarf_loc_regs_t regs;
-  if (!section->GetCfaLocationInfo(rel_pc, fde, &regs)) {
-    return DWARF_LOCATION_INVALID;
-  }
-
-  auto reg_entry = regs.find(fde->cie->return_address_register);
-  if (reg_entry == regs.end()) {
-    return DWARF_LOCATION_INVALID;
-  }
-  return reg_entry->second.type;
-}
-
-static void VerifyReturnAddress(const FrameData& frame) {
-  // Now go and find information about the register data and verify that the relative pc results in
-  // an undefined register.
-  Elf elf(Memory::CreateFileMemory(frame.map_name, 0).release());
-  ASSERT_TRUE(elf.Init()) << "Failed to init elf object from " << frame.map_name;
-  ASSERT_TRUE(elf.valid()) << "Elf " << frame.map_name << " is not valid.";
-  ElfInterface* interface = elf.interface();
-
-  // Only check the eh_frame and the debug_frame since the undefined register
-  // is set using a cfi directive.
-  // Check debug_frame first, then eh_frame since debug_frame always
-  // contains the most specific data.
-  DwarfLocationEnum location = GetReturnAddressLocation(frame.rel_pc, interface->debug_frame());
-  if (location == DWARF_LOCATION_UNDEFINED) {
-    return;
-  }
-
-  location = GetReturnAddressLocation(frame.rel_pc, interface->eh_frame());
-  ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location);
-}
-
-// This test assumes that it starts from the main thread, and that the
-// libc.so on device will include symbols so that function names can
-// be resolved.
-TEST(VerifyBionicTermination, local_terminate) {
-  std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
-
-  UnwinderFromPid unwinder(512, getpid());
-  ASSERT_TRUE(unwinder.Init(regs->Arch()));
-  unwinder.SetRegs(regs.get());
-
-  RegsGetLocal(regs.get());
-  unwinder.Unwind();
-  ASSERT_LT(0U, unwinder.NumFrames());
-
-  SCOPED_TRACE(DumpFrames(unwinder));
-
-  // Look for the frame that includes __libc_init, there should only
-  // be one and it should be the last.
-  bool found = false;
-  const std::vector<FrameData>& frames = unwinder.frames();
-  for (size_t i = 0; i < unwinder.NumFrames(); i++) {
-    const FrameData& frame = frames[i];
-    if (frame.function_name == "__libc_init" && !frame.map_name.empty() &&
-        std::string("libc.so") == basename(frame.map_name.c_str())) {
-      ASSERT_EQ(unwinder.NumFrames(), i + 1) << "__libc_init is not last frame.";
-      ASSERT_NO_FATAL_FAILURE(VerifyReturnAddress(frame));
-      found = true;
-    }
-  }
-  ASSERT_TRUE(found) << "Unable to find libc.so:__libc_init frame\n";
-}
-
-}  // namespace unwindstack
-
-#endif
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
deleted file mode 100644
index f3eb615..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt b/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
deleted file mode 100644
index 7d52483..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-eb503000-eb5e8000 r-xp 0 00:00 0   libc.so
-eb831000-eb852000 r-xp 0 00:00 0   tombstoned
-ffffe000-fffff000 r-xp 0 00:00 0   vdso.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt b/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
deleted file mode 100644
index 821928e..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-eax: fffffffc
-ebx: 4
-ecx: eb290180
-edx: 20
-ebp: 8
-edi: 0
-esi: ffffffff
-esp: fffe1a30
-eip: ffffe430
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data b/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
deleted file mode 100644
index b95bfac..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned b/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
deleted file mode 100644
index aefdb6b..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
deleted file mode 100644
index c71dcfb..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
+++ /dev/null
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
deleted file mode 100644
index 022404c..0000000
--- a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
+++ /dev/null
@@ -1 +0,0 @@
-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
deleted file mode 100644
index b7f10ef..0000000
--- a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-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/load_bias_different_section_bias_arm64/libc.so b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
deleted file mode 100644
index 7bb7156..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64 b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
deleted file mode 100644
index 00a3896..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
deleted file mode 100644
index a2babee..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-5f73997000-5f739dc000 r--p 0 00:00 0   test
-5f739dc000-5f73a43000 r-xp 44000 00:00 0   test
-711152c000-711156e000 r--p 0 00:00 0   libc.so
-711156e000-7111611000 --xp 42000 00:00 0   libc.so
-7112be2000-7112be4000 r-xp 0 00:00 0   vdso
-7112be4000-7112c1c000 r--p 0 00:00 0   linker64
-7112c1c000-7112ce1000 r-xp 38000 00:00 0   linker64
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
deleted file mode 100644
index 3c601e1..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-x0: 7112bdbc24
-x1: 0
-x2: ffffffff
-x3: 0
-x4: 0
-x5: 0
-x6: 0
-x7: 7f7f7f7f7f7f7f7f
-x8: 62
-x9: a78826643b37f4a1
-x10: 7112bdbc20
-x11: 4100
-x12: 7112bdbb70
-x13: 18
-x14: 1d6518077
-x15: 2a43148faf732a
-x16: 16fc0
-x17: 71115f61a0
-x18: 7111d6a000
-x19: 7112cef1b0
-x20: 7112bdbda0
-x21: 59616d61
-x22: 1
-x23: 7112bdbc24
-x24: 4b0e
-x25: 62
-x26: 2
-x27: 0
-x28: 7111934020
-x29: 7112bdbd90
-sp: 7112bdbbf0
-lr: 7112c394ec
-pc: 7112cb99bc
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
deleted file mode 100644
index 6d7b48a..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
deleted file mode 100644
index 3a75b8f..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
deleted file mode 100644
index 4940916..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
deleted file mode 100644
index 63383d0..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
deleted file mode 100644
index ba5a31b..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-200000-919000 r--p 0 00:00 0   perfetto_unittests
-919000-1a0c000 r-xp 719000 00:00 0   perfetto_unittests
-7f932696e000-7f9326b23000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
deleted file mode 100644
index a30e599..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
deleted file mode 100644
index 6cb4055..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-rax: 3b
-rbx: 3b
-rcx: 7f9326a57dd4
-rdx: 3b
-r8: 7ffd22415b09
-r9: 7ffd224155e0
-r10: 0
-r11: 246
-r12: 7f9326d28760
-r13: 3b
-r14: 7f9326d23760
-r15: 3b
-rdi: 1
-rsi: 2678850
-rbp: 2678850
-rsp: 7ffd224153c8
-rip: 7f9326a57dd4
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
deleted file mode 100644
index 4edfe07..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 64b58a8..4f67d67 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -275,9 +275,6 @@
 
     if (maps_by_start.count(frame.map_start) == 0) {
       map_info = maps->Find(frame.map_start);
-      if (map_info == nullptr) {
-        continue;
-      }
 
       auto info = FillInAndGetMapInfo(maps_by_start, map_info);
       bool file_copied = false;
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 7a6d8ba..92e5c0a 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -31,7 +31,6 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Log.h>
-#include <unwindstack/Memory.h>
 
 #include "ArmExidx.h"
 #include "ElfInterfaceArm.h"
@@ -106,7 +105,14 @@
   // Send all log messages to stdout.
   log_to_stdout(true);
 
-  Elf elf(Memory::CreateFileMemory(file, offset).release());
+  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+  if (!memory->Init(file, offset)) {
+    // Initializatation failed.
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  Elf elf(memory);
   if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 0cbcac5..b77a86b 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -33,7 +33,6 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/ElfInterface.h>
 #include <unwindstack/Log.h>
-#include <unwindstack/Memory.h>
 
 #include "ArmExidx.h"
 #include "DwarfOp.h"
@@ -165,8 +164,15 @@
   }
 }
 
-int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
-  Elf elf(Memory::CreateFileMemory(file, offset).release());
+int GetInfo(const char* file, uint64_t pc) {
+  MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+  if (!memory->Init(file, 0)) {
+    // Initializatation failed.
+    printf("Failed to init\n");
+    return 1;
+  }
+
+  Elf elf(memory);
   if (!elf.Init() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", file);
     return 1;
@@ -199,7 +205,7 @@
   DwarfSection* section = interface->eh_frame();
   if (section != nullptr) {
     printf("\neh_frame:\n");
-    PrintRegInformation(section, elf.memory(), pc, elf.class_type());
+    PrintRegInformation(section, memory, pc, elf.class_type());
   } else {
     printf("\nno eh_frame information\n");
   }
@@ -207,7 +213,7 @@
   section = interface->debug_frame();
   if (section != nullptr) {
     printf("\ndebug_frame:\n");
-    PrintRegInformation(section, elf.memory(), pc, elf.class_type());
+    PrintRegInformation(section, memory, pc, elf.class_type());
     printf("\n");
   } else {
     printf("\nno debug_frame information\n");
@@ -243,14 +249,12 @@
 }  // namespace unwindstack
 
 int main(int argc, char** argv) {
-  if (argc != 3 && argc != 4) {
-    printf("Usage: unwind_reg_info ELF_FILE PC [OFFSET]\n");
+  if (argc != 3) {
+    printf("Usage: unwind_reg_info ELF_FILE PC\n");
     printf("  ELF_FILE\n");
     printf("    The path to an elf file.\n");
     printf("  PC\n");
     printf("    The pc for which the register information should be obtained.\n");
-    printf("  OFFSET\n");
-    printf("    Use the offset into the ELF file as the beginning of the elf.\n");
     return 1;
   }
 
@@ -272,15 +276,5 @@
     return 1;
   }
 
-  uint64_t offset = 0;
-  if (argc == 4) {
-    char* end;
-    offset = strtoull(argv[3], &end, 16);
-    if (*end != '\0') {
-      printf("Malformed OFFSET value: %s\n", argv[3]);
-      return 1;
-    }
-  }
-
-  return unwindstack::GetInfo(argv[1], offset, pc);
+  return unwindstack::GetInfo(argv[1], pc);
 }
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 8df2284..b0a4dd0 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -59,7 +59,13 @@
   // Send all log messages to stdout.
   unwindstack::log_to_stdout(true);
 
-  unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(argv[1], 0).release());
+  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() || !elf.valid()) {
     printf("%s is not a valid elf file.\n", argv[1]);
     return 1;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 98921be..4f194c7 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -17,7 +17,6 @@
     vendor_available: true,
     recovery_available: true,
     host_supported: true,
-    native_bridge_supported: true,
 
     header_libs: [
         "liblog_headers",
@@ -122,7 +121,6 @@
 cc_library {
     name: "libutils",
     defaults: ["libutils_defaults"],
-    native_bridge_supported: true,
 
     srcs: [
         "FileMap.cpp",
@@ -205,7 +203,6 @@
         "Mutex_test.cpp",
         "SharedBuffer_test.cpp",
         "String8_test.cpp",
-        "String16_test.cpp",
         "StrongPointer_test.cpp",
         "Unicode_test.cpp",
         "Vector_test.cpp",
@@ -290,9 +287,3 @@
     ],
     shared_libs: ["libutils_test_singleton1"],
 }
-
-cc_benchmark {
-    name: "libutils_benchmark",
-    srcs: ["Vector_benchmark.cpp"],
-    shared_libs: ["libutils"],
-}
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 14e3e35..2d696eb 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -14,9 +14,7 @@
 #define DEBUG_CALLBACKS 0
 
 #include <utils/Looper.h>
-
 #include <sys/eventfd.h>
-#include <cinttypes>
 
 namespace android {
 
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 37bdf05..6fdc0edc 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -11,9 +11,8 @@
 
 #include <utils/threads.h>
 
-// b/141212746 - increased for virtual platforms with higher volatility
 // # of milliseconds to fudge stopwatch measurements
-#define TIMING_TOLERANCE_MS 100
+#define TIMING_TOLERANCE_MS 25
 
 namespace android {
 
diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp
index 3e703db..7910c6e 100644
--- a/libutils/SharedBuffer.cpp
+++ b/libutils/SharedBuffer.cpp
@@ -41,7 +41,6 @@
         // The following is OK on Android-supported platforms.
         sb->mRefs.store(1, std::memory_order_relaxed);
         sb->mSize = size;
-        sb->mClientMetadata = 0;
     }
     return sb;
 }
diff --git a/libutils/SharedBuffer.h b/libutils/SharedBuffer.h
index 476c842..fdf13a9 100644
--- a/libutils/SharedBuffer.h
+++ b/libutils/SharedBuffer.h
@@ -102,12 +102,7 @@
         // Must be sized to preserve correct alignment.
         mutable std::atomic<int32_t>        mRefs;
                 size_t                      mSize;
-                uint32_t                    mReserved;
-public:
-        // mClientMetadata is reserved for client use.  It is initialized to 0
-        // and the clients can do whatever they want with it.  Note that this is
-        // placed last so that it is adjcent to the buffer allocated.
-                uint32_t                    mClientMetadata;
+                uint32_t                    mReserved[2];
 };
 
 static_assert(sizeof(SharedBuffer) % 8 == 0
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index e2a8c59..818b171 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -24,21 +24,21 @@
 
 namespace android {
 
-static const StaticString16 emptyString(u"");
 static inline char16_t* getEmptyString() {
-    return const_cast<char16_t*>(emptyString.string());
+    static SharedBuffer* gEmptyStringBuf = [] {
+        SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
+        char16_t* str = static_cast<char16_t*>(buf->data());
+        *str = 0;
+        return buf;
+    }();
+
+    gEmptyStringBuf->acquire();
+    return static_cast<char16_t*>(gEmptyStringBuf->data());
 }
 
 // ---------------------------------------------------------------------------
 
-void* String16::alloc(size_t size)
-{
-    SharedBuffer* buf = SharedBuffer::alloc(size);
-    buf->mClientMetadata = kIsSharedBufferAllocated;
-    return buf;
-}
-
-char16_t* String16::allocFromUTF8(const char* u8str, size_t u8len)
+static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
 {
     if (u8len == 0) return getEmptyString();
 
@@ -49,7 +49,7 @@
         return getEmptyString();
     }
 
-    SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t) * (u16len + 1)));
+    SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1));
     if (buf) {
         u8cur = (const uint8_t*) u8str;
         char16_t* u16str = (char16_t*)buf->data();
@@ -66,13 +66,13 @@
     return getEmptyString();
 }
 
-char16_t* String16::allocFromUTF16(const char16_t* u16str, size_t u16len) {
+static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len) {
     if (u16len >= SIZE_MAX / sizeof(char16_t)) {
         android_errorWriteLog(0x534e4554, "73826242");
         abort();
     }
 
-    SharedBuffer* buf = static_cast<SharedBuffer*>(alloc((u16len + 1) * sizeof(char16_t)));
+    SharedBuffer* buf = SharedBuffer::alloc((u16len + 1) * sizeof(char16_t));
     ALOG_ASSERT(buf, "Unable to allocate shared buffer");
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
@@ -97,8 +97,8 @@
     // having run. In this case we always allocate an empty string. It's less
     // efficient than using getEmptyString(), but we assume it's uncommon.
 
-    SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t)));
-    char16_t* data = static_cast<char16_t*>(buf->data());
+    char16_t* data = static_cast<char16_t*>(
+            SharedBuffer::alloc(sizeof(char16_t))->data());
     data[0] = 0;
     mString = data;
 }
@@ -106,7 +106,7 @@
 String16::String16(const String16& o)
     : mString(o.mString)
 {
-    acquire();
+    SharedBuffer::bufferFromData(mString)->acquire();
 }
 
 String16::String16(const String16& o, size_t len, size_t begin)
@@ -136,30 +136,26 @@
 
 String16::~String16()
 {
-    release();
+    SharedBuffer::bufferFromData(mString)->release();
 }
 
 size_t String16::size() const
 {
-    if (isStaticString()) {
-        return staticStringSize();
-    } else {
-        return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1;
-    }
+    return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
 }
 
 void String16::setTo(const String16& other)
 {
-    release();
+    SharedBuffer::bufferFromData(other.mString)->acquire();
+    SharedBuffer::bufferFromData(mString)->release();
     mString = other.mString;
-    acquire();
 }
 
 status_t String16::setTo(const String16& other, size_t len, size_t begin)
 {
     const size_t N = other.size();
     if (begin >= N) {
-        release();
+        SharedBuffer::bufferFromData(mString)->release();
         mString = getEmptyString();
         return OK;
     }
@@ -188,7 +184,8 @@
         abort();
     }
 
-    SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
+    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+        ->editResize((len+1)*sizeof(char16_t));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         memmove(str, other, len*sizeof(char16_t));
@@ -215,8 +212,8 @@
         abort();
     }
 
-    SharedBuffer* buf =
-            static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
+    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+        ->editResize((myLen+otherLen+1)*sizeof(char16_t));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
@@ -241,8 +238,8 @@
         abort();
     }
 
-    SharedBuffer* buf =
-            static_cast<SharedBuffer*>(editResize((myLen + otherLen + 1) * sizeof(char16_t)));
+    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+        ->editResize((myLen+otherLen+1)*sizeof(char16_t));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
@@ -276,8 +273,8 @@
            len, myLen, String8(chrs, len).string());
     #endif
 
-    SharedBuffer* buf =
-            static_cast<SharedBuffer*>(editResize((myLen + len + 1) * sizeof(char16_t)));
+    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+        ->editResize((myLen+len+1)*sizeof(char16_t));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         if (pos < myLen) {
@@ -341,85 +338,23 @@
     return strstr16(mString, chrs) != nullptr;
 }
 
-void* String16::edit() {
-    SharedBuffer* buf;
-    if (isStaticString()) {
-        buf = static_cast<SharedBuffer*>(alloc((size() + 1) * sizeof(char16_t)));
-        if (buf) {
-            memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t));
-        }
-    } else {
-        buf = SharedBuffer::bufferFromData(mString)->edit();
-        buf->mClientMetadata = kIsSharedBufferAllocated;
-    }
-    return buf;
-}
-
-void* String16::editResize(size_t newSize) {
-    SharedBuffer* buf;
-    if (isStaticString()) {
-        size_t copySize = (size() + 1) * sizeof(char16_t);
-        if (newSize < copySize) {
-            copySize = newSize;
-        }
-        buf = static_cast<SharedBuffer*>(alloc(newSize));
-        if (buf) {
-            memcpy(buf->data(), mString, copySize);
-        }
-    } else {
-        buf = SharedBuffer::bufferFromData(mString)->editResize(newSize);
-        buf->mClientMetadata = kIsSharedBufferAllocated;
-    }
-    return buf;
-}
-
-void String16::acquire()
-{
-    if (!isStaticString()) {
-        SharedBuffer::bufferFromData(mString)->acquire();
-    }
-}
-
-void String16::release()
-{
-    if (!isStaticString()) {
-        SharedBuffer::bufferFromData(mString)->release();
-    }
-}
-
-bool String16::isStaticString() const {
-    // See String16.h for notes on the memory layout of String16::StaticData and
-    // SharedBuffer.
-    static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
-    const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
-    return (*(p - 1) & kIsSharedBufferAllocated) == 0;
-}
-
-size_t String16::staticStringSize() const {
-    // See String16.h for notes on the memory layout of String16::StaticData and
-    // SharedBuffer.
-    static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);
-    const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);
-    return static_cast<size_t>(*(p - 1));
-}
-
 status_t String16::makeLower()
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edited = nullptr;
+    char16_t* edit = nullptr;
     for (size_t i=0; i<N; i++) {
         const char16_t v = str[i];
         if (v >= 'A' && v <= 'Z') {
-            if (!edited) {
-                SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
+            if (!edit) {
+                SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
                 if (!buf) {
                     return NO_MEMORY;
                 }
-                edited = (char16_t*)buf->data();
-                mString = str = edited;
+                edit = (char16_t*)buf->data();
+                mString = str = edit;
             }
-            edited[i] = tolower((char)v);
+            edit[i] = tolower((char)v);
         }
     }
     return OK;
@@ -429,18 +364,18 @@
 {
     const size_t N = size();
     const char16_t* str = string();
-    char16_t* edited = nullptr;
+    char16_t* edit = nullptr;
     for (size_t i=0; i<N; i++) {
         if (str[i] == replaceThis) {
-            if (!edited) {
-                SharedBuffer* buf = static_cast<SharedBuffer*>(edit());
+            if (!edit) {
+                SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
                 if (!buf) {
                     return NO_MEMORY;
                 }
-                edited = (char16_t*)buf->data();
-                mString = str = edited;
+                edit = (char16_t*)buf->data();
+                mString = str = edit;
             }
-            edited[i] = withThis;
+            edit[i] = withThis;
         }
     }
     return OK;
@@ -450,7 +385,7 @@
 {
     const size_t N = size();
     if (begin >= N) {
-        release();
+        SharedBuffer::bufferFromData(mString)->release();
         mString = getEmptyString();
         return OK;
     }
@@ -460,7 +395,8 @@
     }
 
     if (begin > 0) {
-        SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((N + 1) * sizeof(char16_t)));
+        SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+            ->editResize((N+1)*sizeof(char16_t));
         if (!buf) {
             return NO_MEMORY;
         }
@@ -468,7 +404,8 @@
         memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
         mString = str;
     }
-    SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));
+    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
+        ->editResize((len+1)*sizeof(char16_t));
     if (buf) {
         char16_t* str = (char16_t*)buf->data();
         str[len] = 0;
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
deleted file mode 100644
index f1f24c3..0000000
--- a/libutils/String16_test.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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/String16.h>
-#include <utils/String8.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-
-::testing::AssertionResult Char16_tStringEquals(const char16_t* a, const char16_t* b) {
-    if (strcmp16(a, b) != 0) {
-        return ::testing::AssertionFailure()
-               << "\"" << String8(a).c_str() << "\" not equal to \"" << String8(b).c_str() << "\"";
-    }
-    return ::testing::AssertionSuccess();
-}
-
-#define EXPECT_STR16EQ(a, b) EXPECT_TRUE(Char16_tStringEquals(a, b))
-
-TEST(String16Test, FromChar16_t) {
-    String16 tmp(u"Verify me");
-    EXPECT_STR16EQ(u"Verify me", tmp);
-}
-
-TEST(String16Test, FromChar16_tSized) {
-    String16 tmp(u"Verify me", 7);
-    EXPECT_STR16EQ(u"Verify ", tmp);
-}
-
-TEST(String16Test, FromChar) {
-    String16 tmp("Verify me");
-    EXPECT_STR16EQ(u"Verify me", tmp);
-}
-
-TEST(String16Test, FromCharSized) {
-    String16 tmp("Verify me", 7);
-    EXPECT_STR16EQ(u"Verify ", tmp);
-}
-
-TEST(String16Test, Copy) {
-    String16 tmp("Verify me");
-    String16 another = tmp;
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
-}
-
-TEST(String16Test, Move) {
-    String16 tmp("Verify me");
-    String16 another(std::move(tmp));
-    EXPECT_STR16EQ(u"Verify me", another);
-}
-
-TEST(String16Test, Size) {
-    String16 tmp("Verify me");
-    EXPECT_EQ(9U, tmp.size());
-}
-
-TEST(String16Test, setTo) {
-    String16 tmp("Verify me");
-    tmp.setTo(u"New content");
-    EXPECT_EQ(11U, tmp.size());
-    EXPECT_STR16EQ(u"New content", tmp);
-}
-
-TEST(String16Test, Append) {
-    String16 tmp("Verify me");
-    tmp.append(String16("Hello"));
-    EXPECT_EQ(14U, tmp.size());
-    EXPECT_STR16EQ(u"Verify meHello", tmp);
-}
-
-TEST(String16Test, Insert) {
-    String16 tmp("Verify me");
-    tmp.insert(6, u"Insert");
-    EXPECT_EQ(15U, tmp.size());
-    EXPECT_STR16EQ(u"VerifyInsert me", tmp);
-}
-
-TEST(String16Test, Remove) {
-    String16 tmp("Verify me");
-    tmp.remove(2, 6);
-    EXPECT_EQ(2U, tmp.size());
-    EXPECT_STR16EQ(u" m", tmp);
-}
-
-TEST(String16Test, MakeLower) {
-    String16 tmp("Verify Me!");
-    tmp.makeLower();
-    EXPECT_EQ(10U, tmp.size());
-    EXPECT_STR16EQ(u"verify me!", tmp);
-}
-
-TEST(String16Test, ReplaceAll) {
-    String16 tmp("Verify verify Verify");
-    tmp.replaceAll(u'r', u'!');
-    EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
-}
-
-TEST(String16Test, Compare) {
-    String16 tmp("Verify me");
-    EXPECT_EQ(String16(u"Verify me"), tmp);
-}
-
-TEST(String16Test, StaticString) {
-    String16 nonStaticString("NonStatic");
-    StaticString16 staticString(u"Static");
-
-    EXPECT_TRUE(staticString.isStaticString());
-    EXPECT_FALSE(nonStaticString.isStaticString());
-}
-
-TEST(String16Test, StaticStringCopy) {
-    StaticString16 tmp(u"Verify me");
-    String16 another = tmp;
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
-    EXPECT_TRUE(tmp.isStaticString());
-    EXPECT_TRUE(another.isStaticString());
-}
-
-TEST(String16Test, StaticStringMove) {
-    StaticString16 tmp(u"Verify me");
-    String16 another(std::move(tmp));
-    EXPECT_STR16EQ(u"Verify me", another);
-    EXPECT_TRUE(another.isStaticString());
-}
-
-TEST(String16Test, StaticStringSize) {
-    StaticString16 tmp(u"Verify me");
-    EXPECT_EQ(9U, tmp.size());
-}
-
-TEST(String16Test, StaticStringSetTo) {
-    StaticString16 tmp(u"Verify me");
-    tmp.setTo(u"New content");
-    EXPECT_EQ(11U, tmp.size());
-    EXPECT_STR16EQ(u"New content", tmp);
-    EXPECT_FALSE(tmp.isStaticString());
-}
-
-TEST(String16Test, StaticStringAppend) {
-    StaticString16 tmp(u"Verify me");
-    tmp.append(String16("Hello"));
-    EXPECT_EQ(14U, tmp.size());
-    EXPECT_STR16EQ(u"Verify meHello", tmp);
-    EXPECT_FALSE(tmp.isStaticString());
-}
-
-TEST(String16Test, StaticStringInsert) {
-    StaticString16 tmp(u"Verify me");
-    tmp.insert(6, u"Insert");
-    EXPECT_EQ(15U, tmp.size());
-    EXPECT_STR16EQ(u"VerifyInsert me", tmp);
-    EXPECT_FALSE(tmp.isStaticString());
-}
-
-TEST(String16Test, StaticStringRemove) {
-    StaticString16 tmp(u"Verify me");
-    tmp.remove(2, 6);
-    EXPECT_EQ(2U, tmp.size());
-    EXPECT_STR16EQ(u" m", tmp);
-    EXPECT_FALSE(tmp.isStaticString());
-}
-
-TEST(String16Test, StaticStringMakeLower) {
-    StaticString16 tmp(u"Verify me!");
-    tmp.makeLower();
-    EXPECT_EQ(10U, tmp.size());
-    EXPECT_STR16EQ(u"verify me!", tmp);
-    EXPECT_FALSE(tmp.isStaticString());
-}
-
-TEST(String16Test, StaticStringReplaceAll) {
-    StaticString16 tmp(u"Verify verify Verify");
-    tmp.replaceAll(u'r', u'!');
-    EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
-    EXPECT_FALSE(tmp.isStaticString());
-}
-
-TEST(String16Test, StaticStringCompare) {
-    StaticString16 tmp(u"Verify me");
-    EXPECT_EQ(String16(u"Verify me"), tmp);
-}
-
-TEST(String16Test, StringSetToStaticString) {
-    StaticString16 tmp(u"Verify me");
-    String16 another(u"nonstatic");
-    another = tmp;
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
-}
-
-TEST(String16Test, StringMoveFromStaticString) {
-    StaticString16 tmp(u"Verify me");
-    String16 another(std::move(tmp));
-    EXPECT_STR16EQ(u"Verify me", another);
-}
-
-TEST(String16Test, EmptyStringIsStatic) {
-    String16 tmp("");
-    EXPECT_TRUE(tmp.isStaticString());
-}
-
-}  // namespace android
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index d13548e..0025c56 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -468,6 +468,21 @@
     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
 
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 1172ae7..c3641ef 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -22,8 +22,7 @@
 #include <limits.h>
 #include <time.h>
 
-// host linux support requires Linux 2.6.39+
-#if defined(__linux__)
+#if defined(__ANDROID__)
 nsecs_t systemTime(int clock)
 {
     static const clockid_t clocks[] = {
@@ -42,7 +41,8 @@
 nsecs_t systemTime(int /*clock*/)
 {
     // Clock support varies widely across hosts. Mac OS doesn't support
-    // CLOCK_BOOTTIME, and Windows is windows.
+    // posix clocks, older glibcs don't support CLOCK_BOOTTIME and Windows
+    // is windows.
     struct timeval t;
     t.tv_sec = t.tv_usec = 0;
     gettimeofday(&t, nullptr);
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index b08e061..24a745a 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -452,6 +452,48 @@
     *codePoint |= 0x3F & byte;
 }
 
+size_t utf8_to_utf32_length(const char *src, size_t src_len)
+{
+    if (src == nullptr || src_len == 0) {
+        return 0;
+    }
+    size_t ret = 0;
+    const char* cur;
+    const char* end;
+    size_t num_to_skip;
+    for (cur = src, end = src + src_len, num_to_skip = 1;
+         cur < end;
+         cur += num_to_skip, ret++) {
+        const char first_char = *cur;
+        num_to_skip = 1;
+        if ((first_char & 0x80) == 0) {  // ASCII
+            continue;
+        }
+        int32_t mask;
+
+        for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) {
+        }
+    }
+    return ret;
+}
+
+void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
+{
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
+        return;
+    }
+
+    const char* cur = src;
+    const char* const end = src + src_len;
+    char32_t* cur_utf32 = dst;
+    while (cur < end) {
+        size_t num_read;
+        *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read));
+        cur += num_read;
+    }
+    *cur_utf32 = 0;
+}
+
 static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length)
 {
     uint32_t unicode;
diff --git a/libutils/Vector_benchmark.cpp b/libutils/Vector_benchmark.cpp
deleted file mode 100644
index c23d499..0000000
--- a/libutils/Vector_benchmark.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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 <benchmark/benchmark.h>
-#include <utils/Vector.h>
-#include <vector>
-
-void BM_fill_android_vector(benchmark::State& state) {
-    android::Vector<char> v;
-    while (state.KeepRunning()) {
-        v.push('A');
-    }
-}
-BENCHMARK(BM_fill_android_vector);
-
-void BM_fill_std_vector(benchmark::State& state) {
-    std::vector<char> v;
-    while (state.KeepRunning()) {
-        v.push_back('A');
-    }
-}
-BENCHMARK(BM_fill_std_vector);
-
-void BM_prepend_android_vector(benchmark::State& state) {
-    android::Vector<char> v;
-    while (state.KeepRunning()) {
-        v.insertAt('A', 0);
-    }
-}
-BENCHMARK(BM_prepend_android_vector);
-
-void BM_prepend_std_vector(benchmark::State& state) {
-    std::vector<char> v;
-    while (state.KeepRunning()) {
-        v.insert(v.begin(), 'A');
-    }
-}
-BENCHMARK(BM_prepend_std_vector);
-
-BENCHMARK_MAIN();
diff --git a/libutils/include/utils/ByteOrder.h b/libutils/include/utils/ByteOrder.h
index 9b940e6..44ea13d 100644
--- a/libutils/include/utils/ByteOrder.h
+++ b/libutils/include/utils/ByteOrder.h
@@ -14,17 +14,10 @@
  * limitations under the License.
  */
 
-#pragma once
+//
 
-/*
- * If you're looking for a portable <endian.h> that's available on Android,
- * Linux, macOS, and Windows, see <android-base/endian.h> instead.
- *
- * Nothing in this file is useful because all supported Android ABIs are
- * little-endian and all our code that runs on the host assumes that the host is
- * also little-endian. What pretense at big-endian support exists is completely
- * untested and unlikely to actually work.
- */
+#ifndef _LIBS_UTILS_BYTE_ORDER_H
+#define _LIBS_UTILS_BYTE_ORDER_H
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -34,12 +27,55 @@
 #include <netinet/in.h>
 #endif
 
-/* TODO: move this cruft to frameworks/. */
+/*
+ * These macros are like the hton/ntoh byte swapping macros,
+ * except they allow you to swap to and from the "device" byte
+ * order.  The device byte order is the endianness of the target
+ * device -- for the ARM CPUs we use today, this is little endian.
+ *
+ * Note that the byte swapping functions have not been optimized
+ * much; performance is currently not an issue for them since the
+ * intent is to allow us to avoid byte swapping on the device.
+ */
 
-#define dtohl(x) (x)
-#define dtohs(x) (x)
-#define htodl(x) (x)
-#define htods(x) (x)
+static inline uint32_t android_swap_long(uint32_t v)
+{
+    return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24);
+}
 
+static inline uint16_t android_swap_short(uint16_t v)
+{
+    return (v<<8) | (v>>8);
+}
+
+#define DEVICE_BYTE_ORDER LITTLE_ENDIAN
+
+#if BYTE_ORDER == DEVICE_BYTE_ORDER
+
+#define	dtohl(x)	(x)
+#define	dtohs(x)	(x)
+#define	htodl(x)	(x)
+#define	htods(x)	(x)
+
+#else
+
+#define	dtohl(x)	(android_swap_long(x))
+#define	dtohs(x)	(android_swap_short(x))
+#define	htodl(x)	(android_swap_long(x))
+#define	htods(x)	(android_swap_short(x))
+
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
 #define fromlel(x) (x)
+#define fromles(x) (x)
 #define tolel(x) (x)
+#define toles(x) (x)
+#else
+#define fromlel(x) (android_swap_long(x))
+#define fromles(x) (android_swap_short(x))
+#define tolel(x) (android_swap_long(x))
+#define toles(x) (android_swap_short(x))
+#endif
+
+#endif // _LIBS_UTILS_BYTE_ORDER_H
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 17c5e10..2c4b807 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -17,9 +17,6 @@
 #ifndef ANDROID_UTILS_FLATTENABLE_H
 #define ANDROID_UTILS_FLATTENABLE_H
 
-// DO NOT USE: please use parcelable instead
-// This code is deprecated and will not be supported via AIDL code gen. For data
-// to be sent over binder, please use parcelables.
 
 #include <stdint.h>
 #include <string.h>
@@ -31,9 +28,7 @@
 
 namespace android {
 
-// DO NOT USE: please use parcelable instead
-// This code is deprecated and will not be supported via AIDL code gen. For data
-// to be sent over binder, please use parcelables.
+
 class FlattenableUtils {
 public:
     template<size_t N>
@@ -89,9 +84,7 @@
     }
 };
 
-// DO NOT USE: please use parcelable instead
-// This code is deprecated and will not be supported via AIDL code gen. For data
-// to be sent over binder, please use parcelables.
+
 /*
  * The Flattenable protocol allows an object to serialize itself out
  * to a byte-buffer and an array of file descriptors.
@@ -143,9 +136,6 @@
     return static_cast<T*>(this)->T::unflatten(buffer, size, fds, count);
 }
 
-// DO NOT USE: please use parcelable instead
-// This code is deprecated and will not be supported via AIDL code gen. For data
-// to be sent over binder, please use parcelables.
 /*
  * LightFlattenable is a protocol allowing object to serialize themselves out
  * to a byte-buffer. Because it doesn't handle file-descriptors,
@@ -186,9 +176,6 @@
     return static_cast<T*>(this)->T::unflatten(buffer, size);
 }
 
-// DO NOT USE: please use parcelable instead
-// This code is deprecated and will not be supported via AIDL code gen. For data
-// to be sent over binder, please use parcelables.
 /*
  * LightFlattenablePod is an implementation of the LightFlattenable protocol
  * for POD (plain-old-data) objects.
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
index b04e5c1..e488e60 100644
--- a/libutils/include/utils/LightRefBase.h
+++ b/libutils/include/utils/LightRefBase.h
@@ -47,6 +47,8 @@
         return mCount.load(std::memory_order_relaxed);
     }
 
+    typedef LightRefBase<T> basetype;
+
 protected:
     inline ~LightRefBase() { }
 
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index 42c6efb..a105474 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -188,6 +188,9 @@
 // ---------------------------------------------------------------------------
 namespace android {
 
+class TextOutput;
+TextOutput& printWeakPointer(TextOutput& to, const void* val);
+
 // ---------------------------------------------------------------------------
 
 #define COMPARE_WEAK(_op_)                                      \
@@ -296,6 +299,8 @@
         getWeakRefs()->trackMe(enable, retain); 
     }
 
+    typedef RefBase basetype;
+
 protected:
                             RefBase();
     virtual                 ~RefBase();
@@ -454,6 +459,9 @@
     weakref_type*   m_refs;
 };
 
+template <typename T>
+TextOutput& operator<<(TextOutput& to, const wp<T>& val);
+
 #undef COMPARE_WEAK
 
 // ---------------------------------------------------------------------------
@@ -627,6 +635,12 @@
     }
 }
 
+template <typename T>
+inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
+{
+    return printWeakPointer(to, val.unsafe_get());
+}
+
 // ---------------------------------------------------------------------------
 
 // this class just serves as a namespace so TYPE::moveReferences can stay
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index adc3e7d..afbc2ed 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -37,17 +37,13 @@
 
 class String8;
 
-template <size_t N>
-class StaticString16;
-
 // DO NOT USE: please use std::u16string
 
 //! This is a string holding UTF-16 characters.
 class String16
 {
 public:
-    /*
-     * Use String16(StaticLinkage) if you're statically linking against
+    /* use String16(StaticLinkage) if you're statically linking against
      * libutils and declaring an empty static String16, e.g.:
      *
      *   static String16 sAStaticEmptyString(String16::kEmptyString);
@@ -127,76 +123,8 @@
 
     inline                      operator const char16_t*() const;
 
-    // Static and non-static String16 behave the same for the users, so
-    // this method isn't of much use for the users. It is public for testing.
-            bool                isStaticString() const;
-
-  private:
-    /*
-     * A flag indicating the type of underlying buffer.
-     */
-    static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000;
-
-    /*
-     * alloc() returns void* so that SharedBuffer class is not exposed.
-     */
-    static void* alloc(size_t size);
-    static char16_t* allocFromUTF8(const char* u8str, size_t u8len);
-    static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len);
-
-    /*
-     * edit() and editResize() return void* so that SharedBuffer class
-     * is not exposed.
-     */
-    void* edit();
-    void* editResize(size_t new_size);
-
-    void acquire();
-    void release();
-
-    size_t staticStringSize() const;
-
-    const char16_t* mString;
-
-protected:
-    /*
-     * Data structure used to allocate static storage for static String16.
-     *
-     * Note that this data structure and SharedBuffer are used interchangably
-     * as the underlying data structure for a String16.  Therefore, the layout
-     * of this data structure must match the part in SharedBuffer that is
-     * visible to String16.
-     */
-    template <size_t N>
-    struct StaticData {
-        // The high bit of 'size' is used as a flag.
-        static_assert(N - 1 < kIsSharedBufferAllocated, "StaticString16 too long!");
-        constexpr StaticData() : size(N - 1), data{0} {}
-        const uint32_t size;
-        char16_t data[N];
-
-        constexpr StaticData(const StaticData<N>&) = default;
-    };
-
-    /*
-     * Helper function for constructing a StaticData object.
-     */
-    template <size_t N>
-    static constexpr const StaticData<N> makeStaticData(const char16_t (&s)[N]) {
-        StaticData<N> r;
-        // The 'size' field is at the same location where mClientMetadata would
-        // be for a SharedBuffer.  We do NOT set kIsSharedBufferAllocated flag
-        // here.
-        for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i];
-        return r;
-    }
-
-    template <size_t N>
-    explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
-
-public:
-    template <size_t N>
-    explicit constexpr String16(const StaticString16<N>& s) : mString(s.mString) {}
+private:
+            const char16_t*     mString;
 };
 
 // String16 can be trivially moved using memcpy() because moving does not
@@ -204,42 +132,6 @@
 ANDROID_TRIVIAL_MOVE_TRAIT(String16)
 
 // ---------------------------------------------------------------------------
-
-/*
- * A StaticString16 object is a specialized String16 object.  Instead of holding
- * the string data in a ref counted SharedBuffer object, it holds data in a
- * buffer within StaticString16 itself.  Note that this buffer is NOT ref
- * counted and is assumed to be available for as long as there is at least a
- * String16 object using it.  Therefore, one must be extra careful to NEVER
- * assign a StaticString16 to a String16 that outlives the StaticString16
- * object.
- *
- * THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES.
- *
- * A StaticString16 SHOULD NEVER APPEAR IN APIs.  USE String16 INSTEAD.
- */
-template <size_t N>
-class StaticString16 : public String16 {
-public:
-    constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {}
-
-    constexpr StaticString16(const StaticString16<N>& other)
-        : String16(mData), mData(other.mData) {}
-
-    constexpr StaticString16(const StaticString16<N>&&) = delete;
-
-    // There is no reason why one would want to 'new' a StaticString16.  Delete
-    // it to discourage misuse.
-    static void* operator new(std::size_t) = delete;
-
-private:
-    const StaticData<N> mData;
-};
-
-template <typename F>
-StaticString16(const F&)->StaticString16<sizeof(F) / sizeof(char16_t)>;
-
-// ---------------------------------------------------------------------------
 // No user servicable parts below.
 
 inline int compare_type(const String16& lhs, const String16& rhs)
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 0ddcbb2..c8f584e 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -95,6 +95,13 @@
                     __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);
 
diff --git a/libutils/include/utils/Trace.h b/libutils/include/utils/Trace.h
index 9986bf5..4b9c91e 100644
--- a/libutils/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -17,12 +17,7 @@
 #ifndef ANDROID_TRACE_H
 #define ANDROID_TRACE_H
 
-#if defined(_WIN32)
-
-#define ATRACE_NAME(...)
-#define ATRACE_CALL()
-
-#else  // !_WIN32
+#if defined(__ANDROID__)
 
 #include <stdint.h>
 
@@ -33,7 +28,7 @@
 // ATRACE_NAME traces from its location until the end of its enclosing scope.
 #define _PASTE(x, y) x ## y
 #define PASTE(x, y) _PASTE(x,y)
-#define ATRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name)
+#define ATRACE_NAME(name) android::ScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
 
 // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
 #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
@@ -56,6 +51,11 @@
 
 }  // namespace android
 
-#endif  // _WIN32
+#else // !__ANDROID__
+
+#define ATRACE_NAME(...)
+#define ATRACE_CALL()
+
+#endif // __ANDROID__
 
 #endif // ANDROID_TRACE_H
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index fc6712d..a2aaa47 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -129,6 +129,18 @@
 ssize_t utf8_length(const char *src);
 
 /**
+ * Measure the length of a UTF-32 string.
+ */
+size_t utf8_to_utf32_length(const char *src, size_t src_len);
+
+/**
+ * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
+ * enough to store the entire converted string as measured by
+ * utf8_to_utf32_length plus space for a NUL terminator.
+ */
+void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
+
+/**
  * Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case
  * it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you
  * can ask to log a message and fail in case the invalid utf8 could have caused an override if no
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index f4544a1..546c15c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -2,8 +2,7 @@
 
 cc_library {
     name: "libvndksupport",
-    native_bridge_supported: true,
-    srcs: ["linker.cpp"],
+    srcs: ["linker.c"],
     cflags: [
         "-Wall",
         "-Werror",
@@ -23,7 +22,6 @@
 
 llndk_library {
     name: "libvndksupport",
-    native_bridge_supported: true,
     symbol_file: "libvndksupport.map.txt",
     export_include_dirs: ["include"],
 }
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
new file mode 100644
index 0000000..84c2132
--- /dev/null
+++ b/libvndksupport/linker.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "linker.h"
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+
+#define LOG_TAG "vndksupport"
+#include <log/log.h>
+#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*);
+
+static const char* namespace_name = NULL;
+
+static struct android_namespace_t* get_vendor_namespace() {
+    const char* namespace_names[] = {"sphal", "default", NULL};
+    static struct android_namespace_t* vendor_namespace = NULL;
+    if (vendor_namespace == NULL) {
+        int name_idx = 0;
+        while (namespace_names[name_idx] != NULL) {
+            if (android_get_exported_namespace != NULL) {
+                vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
+            }
+            if (vendor_namespace != NULL) {
+                namespace_name = namespace_names[name_idx];
+                break;
+            }
+            name_idx++;
+        }
+    }
+    return vendor_namespace;
+}
+
+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) {
+        const android_dlextinfo dlextinfo = {
+            .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace,
+        };
+        void* handle = NULL;
+        if (android_dlopen_ext != NULL) {
+            handle = android_dlopen_ext(name, flag, &dlextinfo);
+        }
+        if (!handle) {
+            ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror());
+        }
+        return handle;
+    } else {
+        ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
+        return dlopen(name, flag);
+    }
+}
+
+int android_unload_sphal_library(void* handle) {
+    return dlclose(handle);
+}
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
deleted file mode 100644
index cf0f618..0000000
--- a/libvndksupport/linker.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.
- */
-
-#define LOG_TAG "vndksupport"
-
-#include "linker.h"
-
-#include <android/dlext.h>
-#include <dlfcn.h>
-#include <log/log.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <initializer_list>
-
-__attribute__((weak)) extern "C" android_namespace_t* android_get_exported_namespace(const char*);
-__attribute__((weak)) extern "C" void* android_dlopen_ext(const char*, int,
-                                                          const android_dlextinfo*);
-
-namespace {
-
-struct VendorNamespace {
-    android_namespace_t* ptr = nullptr;
-    const char* name = nullptr;
-};
-
-}  // anonymous namespace
-
-static VendorNamespace get_vendor_namespace() {
-    static VendorNamespace result = ([] {
-        for (const char* name : {"sphal", "default"}) {
-            if (android_get_exported_namespace != nullptr) {
-                if (android_namespace_t* ns = android_get_exported_namespace(name)) {
-                    return VendorNamespace{ns, name};
-                }
-            }
-        }
-        return VendorNamespace{};
-    })();
-    return result;
-}
-
-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 == nullptr) {
-        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") == nullptr;
-}
-
-void* android_load_sphal_library(const char* name, int flag) {
-    VendorNamespace vendor_namespace = get_vendor_namespace();
-    if (vendor_namespace.ptr != nullptr) {
-        const android_dlextinfo dlextinfo = {
-                .flags = ANDROID_DLEXT_USE_NAMESPACE,
-                .library_namespace = vendor_namespace.ptr,
-        };
-        void* handle = nullptr;
-        if (android_dlopen_ext != nullptr) {
-            handle = android_dlopen_ext(name, flag, &dlextinfo);
-        }
-        if (!handle) {
-            ALOGE("Could not load %s from %s namespace: %s.", name, vendor_namespace.name,
-                  dlerror());
-        }
-        return handle;
-    } else {
-        ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
-        return dlopen(name, flag);
-    }
-}
-
-int android_unload_sphal_library(void* handle) {
-    return dlclose(handle);
-}
diff --git a/libvndksupport/tests/linker_test.cpp b/libvndksupport/tests/linker_test.cpp
index d0c8ef7..7ce27d4 100644
--- a/libvndksupport/tests/linker_test.cpp
+++ b/libvndksupport/tests/linker_test.cpp
@@ -21,6 +21,11 @@
 #include <vndksupport/linker.h>
 #include <string>
 
+// Since the test executable will be in /data and ld.config.txt does not
+// configure sphal namespace for executables in /data, the call to
+// android_load_sphal_library will always fallback to the plain dlopen from the
+// default namespace.
+
 // Let's use libEGL_<chipset>.so as a SP-HAL in test
 static std::string find_sphal_lib() {
     const char* path =
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index e3bb2ab..bc1543b 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -26,8 +26,6 @@
         // 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,
@@ -76,10 +74,6 @@
         "liblog",
     ],
 
-    // for FRIEND_TEST
-    static_libs: ["libgtest_prod"],
-    export_static_lib_headers: ["libgtest_prod"],
-
     export_include_dirs: ["include"],
 }
 
@@ -88,7 +82,6 @@
     host_supported: true,
     vendor_available: true,
     recovery_available: true,
-    native_bridge_supported: true,
     vndk: {
         enabled: true,
     },
@@ -175,7 +168,7 @@
 }
 
 cc_binary {
-    name: "ziptool",
+    name: "unzip",
     defaults: ["libziparchive_flags"],
     srcs: ["unzip.cpp"],
     shared_libs: [
@@ -183,18 +176,4 @@
         "libziparchive",
     ],
     recovery_available: true,
-    host_supported: true,
-    target: {
-        android: {
-            symlinks: ["unzip", "zipinfo"],
-        },
-    },
-}
-
-cc_fuzz {
-    name: "libziparchive_fuzzer",
-    srcs: ["libziparchive_fuzzer.cpp"],
-    static_libs: ["libziparchive", "libbase", "libz", "liblog"],
-    host_supported: true,
-    corpus: ["testdata/*"],
 }
diff --git a/libziparchive/entry_name_utils-inl.h b/libziparchive/entry_name_utils-inl.h
index 10311b5..1714586 100644
--- a/libziparchive/entry_name_utils-inl.h
+++ b/libziparchive/entry_name_utils-inl.h
@@ -20,15 +20,9 @@
 #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'. They also must
-// fit into the central directory record.
+// Entry names must be valid UTF-8 and must not contain '0'.
 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) {
@@ -41,8 +35,7 @@
       return false;
     } else {
       // 2-5 byte sequences.
-      for (uint8_t first = static_cast<uint8_t>((byte & 0x7f) << 1); first & 0x80;
-           first = static_cast<uint8_t>((first & 0x7f) << 1)) {
+      for (uint8_t first = (byte & 0x7f) << 1; first & 0x80; first = (first & 0x7f) << 1) {
         ++i;
 
         // Missing continuation byte..
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index 047af90..ab38dfd 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -25,9 +25,6 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
-#include <string>
-#include <string_view>
-
 #include "android-base/off64_t.h"
 
 /* Zip compression methods we support */
@@ -36,12 +33,38 @@
   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.
  */
 struct ZipEntry {
-  // Compression method. One of kCompressStored or kCompressDeflated.
-  // See also `gpbf` for deflate subtypes.
+  // Compression method: One of kCompressStored or
+  // kCompressDeflated.
   uint16_t method;
 
   // Modification time. The zipfile format specifies
@@ -55,7 +78,7 @@
   struct tm GetModificationTime() const;
 
   // Suggested Unix mode for this entry, from the zip archive if created on
-  // Unix, or a default otherwise. See also `external_file_attributes`.
+  // Unix, or a default otherwise.
   mode_t unix_mode;
 
   // 1 if this entry contains a data descriptor segment, 0
@@ -79,18 +102,6 @@
 
   // The offset to the start of data for this ZipEntry.
   off64_t offset;
-
-  // The version of zip and the host file system this came from (for zipinfo).
-  uint16_t version_made_by;
-
-  // The raw attributes, whose interpretation depends on the host
-  // file system in `version_made_by` (for zipinfo). See also `unix_mode`.
-  uint32_t external_file_attributes;
-
-  // Specifics about the deflation (for zipinfo).
-  uint16_t gpbf;
-  // Whether this entry is believed to be text or binary (for zipinfo).
-  bool is_text;
 };
 
 struct ZipArchive;
@@ -126,7 +137,7 @@
 int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
                       bool assume_ownership = true);
 
-int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
+int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
                               ZipArchiveHandle* handle);
 /*
  * Close archive, releasing resources associated with it. This will
@@ -137,21 +148,9 @@
  */
 void CloseArchive(ZipArchiveHandle archive);
 
-/** See GetArchiveInfo(). */
-struct ZipArchiveInfo {
-  /** The size in bytes of the archive itself. Used by zipinfo. */
-  off64_t archive_size;
-  /** The number of entries in the archive. */
-  size_t entry_count;
-};
-
-/**
- * Returns information about the given archive.
- */
-ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive);
-
 /*
- * Find an entry in the Zip archive, by name. |data| must be non-null.
+ * 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.
  *
  * Returns 0 if an entry is found, and populates |data| with information
  * about this entry. Returns negative values otherwise.
@@ -165,7 +164,7 @@
  * On non-Windows platforms this method does not modify internal state and
  * can be called concurrently.
  */
-int32_t FindEntry(const ZipArchiveHandle archive, const std::string_view entryName, ZipEntry* data);
+int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data);
 
 /*
  * Start iterating over all entries of a zip file. The order of iteration
@@ -181,8 +180,7 @@
  * Returns 0 on success and negative values on failure.
  */
 int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
-                       const std::string_view optional_prefix = "",
-                       const std::string_view optional_suffix = "");
+                       const ZipString* optional_prefix, const ZipString* optional_suffix);
 
 /*
  * Advance to the next element in the zipfile in iteration order.
@@ -190,8 +188,7 @@
  * 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, std::string* name);
-int32_t Next(void* cookie, ZipEntry* data, std::string_view* name);
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name);
 
 /*
  * End iteration over all entries of a zip file and frees the memory allocated
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index d68683d..f6c8427 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -19,10 +19,8 @@
 #include <cstdio>
 #include <ctime>
 
-#include <gtest/gtest_prod.h>
 #include <memory>
 #include <string>
-#include <string_view>
 #include <vector>
 
 #include "android-base/macros.h"
@@ -78,7 +76,7 @@
     uint32_t uncompressed_size;
     uint16_t last_mod_time;
     uint16_t last_mod_date;
-    uint16_t padding_length;
+    uint32_t padding_length;
     off64_t local_file_header_offset;
   };
 
@@ -103,7 +101,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(std::string_view path, size_t flags);
+  int32_t StartEntry(const char* path, size_t flags);
 
   /**
    * Starts a new zip entry with the given path and flags, where the
@@ -113,17 +111,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(std::string_view path, size_t flags, uint32_t alignment);
+  int32_t StartAlignedEntry(const char* 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(std::string_view path, size_t flags, time_t time);
+  int32_t StartEntryWithTime(const char* 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(std::string_view path, size_t flags, time_t time, uint32_t alignment);
+  int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment);
 
   /**
    * Writes bytes to the zip file for the previously started zip entry.
@@ -163,10 +161,9 @@
 
   int32_t HandleError(int32_t error_code);
   int32_t PrepareDeflate();
-  int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
-  int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
+  int32_t StoreBytes(FileEntry* file, const void* data, size_t len);
+  int32_t CompressBytes(FileEntry* file, const void* data, size_t len);
   int32_t FlushCompressedBytes(FileEntry* file);
-  bool ShouldUseDataDescriptor() const;
 
   enum class State {
     kWritingZip,
@@ -184,6 +181,4 @@
 
   std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
   std::vector<uint8_t> buffer_;
-
-  FRIEND_TEST(zipwriter, WriteToUnseekableFile);
 };
diff --git a/libziparchive/libziparchive_fuzzer.cpp b/libziparchive/libziparchive_fuzzer.cpp
deleted file mode 100644
index 75e7939..0000000
--- a/libziparchive/libziparchive_fuzzer.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <ziparchive/zip_archive.h>
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  ZipArchiveHandle handle = nullptr;
-  OpenArchiveFromMemory(data, size, "fuzz", &handle);
-  CloseArchive(handle);
-  return 0;
-}
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index e4839b4..6756007 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -15,12 +15,10 @@
  */
 
 #include <errno.h>
+#include <error.h>
 #include <fcntl.h>
-#include <fnmatch.h>
 #include <getopt.h>
 #include <inttypes.h>
-#include <libgen.h>
-#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
@@ -35,29 +33,18 @@
 #include <android-base/strings.h>
 #include <ziparchive/zip_archive.h>
 
-using android::base::EndsWith;
-using android::base::StartsWith;
-
 enum OverwriteMode {
   kAlways,
   kNever,
   kPrompt,
 };
 
-enum Role {
-  kUnzip,
-  kZipinfo,
-};
-
-static Role role;
 static OverwriteMode overwrite_mode = kPrompt;
-static bool flag_1 = false;
 static const char* flag_d = nullptr;
 static bool flag_l = false;
 static bool flag_p = false;
 static bool flag_q = false;
 static bool flag_v = false;
-static bool flag_x = false;
 static const char* archive_name = nullptr;
 static std::set<std::string> includes;
 static std::set<std::string> excludes;
@@ -65,35 +52,9 @@
 static uint64_t total_compressed_length = 0;
 static size_t file_count = 0;
 
-static const char* g_progname;
-
-static void die(int error, const char* fmt, ...) {
-  va_list ap;
-
-  va_start(ap, fmt);
-  fprintf(stderr, "%s: ", g_progname);
-  vfprintf(stderr, fmt, ap);
-  if (error != 0) fprintf(stderr, ": %s", strerror(error));
-  fprintf(stderr, "\n");
-  va_end(ap);
-  exit(1);
-}
-
-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;
-  }
+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;
   return false;
 }
 
@@ -111,54 +72,35 @@
 
 static int CompressionRatio(int64_t uncompressed, int64_t compressed) {
   if (uncompressed == 0) return 0;
-  return static_cast<int>((100LL * (uncompressed - compressed)) / uncompressed);
+  return (100LL * (uncompressed - compressed)) / uncompressed;
 }
 
-static void MaybeShowHeader(ZipArchiveHandle zah) {
-  if (role == kUnzip) {
-    // unzip has three formats.
-    if (!flag_q) printf("Archive:  %s\n", archive_name);
-    if (flag_v) {
-      printf(
-          " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
-          "--------  ------  ------- ---- ---------- ----- --------  ----\n");
-    } else if (flag_l) {
-      printf(
-          "  Length      Date    Time    Name\n"
-          "---------  ---------- -----   ----\n");
-    }
-  } else {
-    // zipinfo.
-    if (!flag_1 && includes.empty() && excludes.empty()) {
-      ZipArchiveInfo info{GetArchiveInfo(zah)};
-      printf("Archive:  %s\n", archive_name);
-      printf("Zip file size: %" PRId64 " bytes, number of entries: %zu\n", info.archive_size,
-             info.entry_count);
-    }
+static void MaybeShowHeader() {
+  if (!flag_q) printf("Archive:  %s\n", archive_name);
+  if (flag_v) {
+    printf(
+        " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
+        "--------  ------  ------- ---- ---------- ----- --------  ----\n");
+  } else if (flag_l) {
+    printf(
+        "  Length      Date    Time    Name\n"
+        "---------  ---------- -----   ----\n");
   }
 }
 
 static void MaybeShowFooter() {
-  if (role == kUnzip) {
-    if (flag_v) {
-      printf(
-          "--------          -------  ---                            -------\n"
-          "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
-          total_uncompressed_length, total_compressed_length,
-          CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
-          (file_count == 1) ? "" : "s");
-    } else if (flag_l) {
-      printf(
-          "---------                     -------\n"
-          "%9" PRId64 "                     %zu file%s\n",
-          total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
-    }
-  } else {
-    if (!flag_1 && includes.empty() && excludes.empty()) {
-      printf("%zu files, %" PRId64 " bytes uncompressed, %" PRId64 " bytes compressed: %3d%%\n",
-             file_count, total_uncompressed_length, total_compressed_length,
-             CompressionRatio(total_uncompressed_length, total_compressed_length));
-    }
+  if (flag_v) {
+    printf(
+        "--------          -------  ---                            -------\n"
+        "%8" PRId64 "         %8" PRId64 " %3d%%                            %zu file%s\n",
+        total_uncompressed_length, total_compressed_length,
+        CompressionRatio(total_uncompressed_length, total_compressed_length), file_count,
+        (file_count == 1) ? "" : "s");
+  } else if (flag_l) {
+    printf(
+        "---------                     -------\n"
+        "%9" PRId64 "                     %zu file%s\n",
+        total_uncompressed_length, file_count, (file_count == 1) ? "" : "s");
   }
 }
 
@@ -170,7 +112,7 @@
     char* line = nullptr;
     size_t n;
     if (getline(&line, &n, stdin) == -1) {
-      die(0, "(EOF/read error; assuming [N]one...)");
+      error(1, 0, "(EOF/read error; assuming [N]one...)");
       overwrite_mode = kNever;
       return false;
     }
@@ -198,42 +140,43 @@
   uint8_t* buffer = new uint8_t[entry.uncompressed_length];
   int err = ExtractToMemory(zah, &entry, buffer, entry.uncompressed_length);
   if (err < 0) {
-    die(0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
+    error(1, 0, "failed to extract %s: %s", name.c_str(), ErrorCodeString(err));
   }
   if (!android::base::WriteFully(1, buffer, entry.uncompressed_length)) {
-    die(errno, "failed to write %s to stdout", name.c_str());
+    error(1, errno, "failed to write %s to stdout", name.c_str());
   }
   delete[] buffer;
 }
 
 static void ExtractOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
   // Bad filename?
-  if (StartsWith(name, "/") || StartsWith(name, "../") || name.find("/../") != std::string::npos) {
-    die(0, "bad filename %s", name.c_str());
+  if (android::base::StartsWith(name, "/") || android::base::StartsWith(name, "../") ||
+      name.find("/../") != std::string::npos) {
+    error(1, 0, "bad filename %s", name.c_str());
   }
 
   // Where are we actually extracting to (for human-readable output)?
   std::string dst;
   if (flag_d) {
     dst = flag_d;
-    if (!EndsWith(dst, "/")) dst += '/';
+    if (!android::base::EndsWith(dst, "/")) dst += '/';
   }
   dst += name;
 
   // Ensure the directory hierarchy exists.
   if (!MakeDirectoryHierarchy(android::base::Dirname(name))) {
-    die(errno, "couldn't create directory hierarchy for %s", dst.c_str());
+    error(1, errno, "couldn't create directory hierarchy for %s", dst.c_str());
   }
 
   // An entry in a zip file can just be a directory itself.
-  if (EndsWith(name, "/")) {
+  if (android::base::EndsWith(name, "/")) {
     if (mkdir(name.c_str(), entry.unix_mode) == -1) {
       // If the directory already exists, that's fine.
       if (errno == EEXIST) {
         struct stat sb;
         if (stat(name.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) return;
       }
-      die(errno, "couldn't extract directory %s", dst.c_str());
+      error(1, errno, "couldn't extract directory %s", dst.c_str());
     }
     return;
   }
@@ -246,12 +189,12 @@
     // Either overwrite_mode is kAlways or the user consented to this specific case.
     fd = open(name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, entry.unix_mode);
   }
-  if (fd == -1) die(errno, "couldn't create file %s", dst.c_str());
+  if (fd == -1) error(1, errno, "couldn't create file %s", dst.c_str());
 
   // Actually extract into the file.
   if (!flag_q) printf("  inflating: %s\n", dst.c_str());
   int err = ExtractEntryToFile(zah, &entry, fd);
-  if (err < 0) die(0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
+  if (err < 0) error(1, 0, "failed to extract %s: %s", dst.c_str(), ErrorCodeString(err));
   close(fd);
 }
 
@@ -270,82 +213,17 @@
   }
 }
 
-static void InfoOne(const ZipEntry& entry, const std::string& name) {
-  if (flag_1) {
-    // "android-ndk-r19b/sources/android/NOTICE"
-    printf("%s\n", name.c_str());
-    return;
-  }
-
-  int version = entry.version_made_by & 0xff;
-  int os = (entry.version_made_by >> 8) & 0xff;
-
-  // TODO: Support suid/sgid? Non-Unix/non-FAT host file system attributes?
-  const char* src_fs = "???";
-  char mode[] = "???       ";
-  if (os == 0) {
-    src_fs = "fat";
-    // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
-    int attrs = entry.external_file_attributes & 0xff;
-    mode[0] = (attrs & 0x10) ? 'd' : '-';
-    mode[1] = 'r';
-    mode[2] = (attrs & 0x01) ? '-' : 'w';
-    // The man page also mentions ".btm", but that seems to be obsolete?
-    mode[3] = EndsWith(name, ".exe") || EndsWith(name, ".com") || EndsWith(name, ".bat") ||
-                      EndsWith(name, ".cmd")
-                  ? 'x'
-                  : '-';
-    mode[4] = (attrs & 0x20) ? 'a' : '-';
-    mode[5] = (attrs & 0x02) ? 'h' : '-';
-    mode[6] = (attrs & 0x04) ? 's' : '-';
-  } else if (os == 3) {
-    src_fs = "unx";
-    mode[0] = S_ISDIR(entry.unix_mode) ? 'd' : (S_ISREG(entry.unix_mode) ? '-' : '?');
-    mode[1] = entry.unix_mode & S_IRUSR ? 'r' : '-';
-    mode[2] = entry.unix_mode & S_IWUSR ? 'w' : '-';
-    mode[3] = entry.unix_mode & S_IXUSR ? 'x' : '-';
-    mode[4] = entry.unix_mode & S_IRGRP ? 'r' : '-';
-    mode[5] = entry.unix_mode & S_IWGRP ? 'w' : '-';
-    mode[6] = entry.unix_mode & S_IXGRP ? 'x' : '-';
-    mode[7] = entry.unix_mode & S_IROTH ? 'r' : '-';
-    mode[8] = entry.unix_mode & S_IWOTH ? 'w' : '-';
-    mode[9] = entry.unix_mode & S_IXOTH ? 'x' : '-';
-  }
-
-  char method[5] = "stor";
-  if (entry.method == kCompressDeflated) {
-    snprintf(method, sizeof(method), "def%c", "NXFS"[(entry.gpbf >> 1) & 0x3]);
-  }
-
-  // TODO: zipinfo (unlike unzip) sometimes uses time zone?
-  // TODO: this uses 4-digit years because we're not barbarians unless interoperability forces it.
-  tm t = entry.GetModificationTime();
-  char time[32];
-  snprintf(time, sizeof(time), "%04d-%02d-%02d %02d:%02d", t.tm_year + 1900, t.tm_mon + 1,
-           t.tm_mday, t.tm_hour, t.tm_min);
-
-  // "-rw-r--r--  3.0 unx      577 t- defX 19-Feb-12 16:09 android-ndk-r19b/sources/android/NOTICE"
-  printf("%s %2d.%d %s %8d %c%c %s %s %s\n", mode, version / 10, version % 10, src_fs,
-         entry.uncompressed_length, entry.is_text ? 't' : 'b',
-         entry.has_data_descriptor ? 'X' : 'x', method, time, name.c_str());
-}
-
 static void ProcessOne(ZipArchiveHandle zah, ZipEntry& entry, const std::string& name) {
-  if (role == kUnzip) {
-    if (flag_l || flag_v) {
-      // -l or -lv or -lq or -v.
-      ListOne(entry, name);
-    } else {
-      // Actually extract.
-      if (flag_p) {
-        ExtractToPipe(zah, entry, name);
-      } else {
-        ExtractOne(zah, entry, name);
-      }
-    }
+  if (flag_l || flag_v) {
+    // -l or -lv or -lq or -v.
+    ListOne(entry, name);
   } else {
-    // zipinfo or zipinfo -1.
-    InfoOne(entry, name);
+    // Actually extract.
+    if (flag_p) {
+      ExtractToPipe(zah, entry, name);
+    } else {
+      ExtractOne(zah, entry, name);
+    }
   }
   total_uncompressed_length += entry.uncompressed_length;
   total_compressed_length += entry.compressed_length;
@@ -353,165 +231,112 @@
 }
 
 static void ProcessAll(ZipArchiveHandle zah) {
-  MaybeShowHeader(zah);
+  MaybeShowHeader();
 
   // 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);
+  int err = StartIteration(zah, &cookie, nullptr, nullptr);
   if (err != 0) {
-    die(0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
+    error(1, 0, "couldn't iterate %s: %s", archive_name, ErrorCodeString(err));
   }
 
   ZipEntry entry;
-  std::string name;
-  while ((err = Next(cookie, &entry, &name)) >= 0) {
-    if (ShouldInclude(name)) ProcessOne(zah, entry, name);
+  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);
   }
 
-  if (err < -1) die(0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
+  if (err < -1) error(1, 0, "failed iterating %s: %s", archive_name, ErrorCodeString(err));
   EndIteration(cookie);
 
   MaybeShowFooter();
 }
 
 static void ShowHelp(bool full) {
-  if (role == kUnzip) {
-    fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
-    if (!full) exit(EXIT_FAILURE);
+  fprintf(full ? stdout : stderr, "usage: unzip [-d DIR] [-lnopqv] ZIP [FILE...] [-x FILE...]\n");
+  if (!full) exit(EXIT_FAILURE);
 
-    printf(
-        "\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"
-        "-n	Never overwrite files (default: prompt)\n"
-        "-o	Always overwrite files\n"
-        "-p	Pipe to stdout\n"
-        "-q	Quiet\n"
-        "-v	List contents verbosely\n"
-        "-x FILE	Exclude files\n");
-  } else {
-    fprintf(full ? stdout : stderr, "usage: zipinfo [-1] ZIP [FILE...] [-x FILE...]\n");
-    if (!full) exit(EXIT_FAILURE);
-
-    printf(
-        "\n"
-        "Show information about FILEs from ZIP archive. Default is all files.\n"
-        "Both the include and exclude (-x) lists use shell glob patterns.\n"
-        "\n"
-        "-1	Show filenames only, one per line\n"
-        "-x FILE	Exclude files\n");
-  }
+  printf(
+      "\n"
+      "Extract FILEs from ZIP archive. Default is all files.\n"
+      "\n"
+      "-d DIR	Extract into DIR\n"
+      "-l	List contents (-lq excludes archive name, -lv is verbose)\n"
+      "-n	Never overwrite files (default: prompt)\n"
+      "-o	Always overwrite files\n"
+      "-p	Pipe to stdout\n"
+      "-q	Quiet\n"
+      "-v	List contents verbosely\n"
+      "-x FILE	Exclude files\n");
   exit(EXIT_SUCCESS);
 }
 
-static void HandleCommonOption(int opt) {
-  switch (opt) {
-    case 'h':
-      ShowHelp(true);
-      break;
-    case 'x':
-      flag_x = true;
-      break;
-    case 1:
-      // -x swallows all following arguments, so we use '-' in the getopt
-      // string and collect files here.
-      if (!archive_name) {
-        archive_name = optarg;
-      } else if (flag_x) {
-        excludes.insert(optarg);
-      } else {
-        includes.insert(optarg);
-      }
-      break;
-    default:
-      ShowHelp(false);
-      break;
-  }
-}
-
 int main(int argc, char* argv[]) {
-  // Who am I, and what am I doing?
-  g_progname = basename(argv[0]);
-  if (!strcmp(g_progname, "ziptool") && argc > 1) return main(argc - 1, argv + 1);
-  if (!strcmp(g_progname, "unzip")) {
-    role = kUnzip;
-  } else if (!strcmp(g_progname, "zipinfo")) {
-    role = kZipinfo;
-  } else {
-    die(0, "run as ziptool with unzip or zipinfo as the first argument, or symlink");
-  }
-
-  static const struct option opts[] = {
+  static struct option opts[] = {
       {"help", no_argument, 0, 'h'},
   };
-
-  if (role == kUnzip) {
-    // `unzip -Z` is "zipinfo mode", so in that case just restart...
-    if (argc > 1 && !strcmp(argv[1], "-Z")) {
-      argv[1] = const_cast<char*>("zipinfo");
-      return main(argc - 1, argv + 1);
-    }
-
-    int opt;
-    while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
-      switch (opt) {
-        case 'd':
-          flag_d = optarg;
-          break;
-        case 'l':
-          flag_l = true;
-          break;
-        case 'n':
-          overwrite_mode = kNever;
-          break;
-        case 'o':
-          overwrite_mode = kAlways;
-          break;
-        case 'p':
-          flag_p = flag_q = true;
-          break;
-        case 'q':
-          flag_q = true;
-          break;
-        case 'v':
-          flag_v = true;
-          break;
-        default:
-          HandleCommonOption(opt);
-          break;
-      }
-    }
-  } else {
-    int opt;
-    while ((opt = getopt_long(argc, argv, "-1hx", opts, nullptr)) != -1) {
-      switch (opt) {
-        case '1':
-          flag_1 = true;
-          break;
-        default:
-          HandleCommonOption(opt);
-          break;
-      }
+  bool saw_x = false;
+  int opt;
+  while ((opt = getopt_long(argc, argv, "-d:hlnopqvx", opts, nullptr)) != -1) {
+    switch (opt) {
+      case 'd':
+        flag_d = optarg;
+        break;
+      case 'h':
+        ShowHelp(true);
+        break;
+      case 'l':
+        flag_l = true;
+        break;
+      case 'n':
+        overwrite_mode = kNever;
+        break;
+      case 'o':
+        overwrite_mode = kAlways;
+        break;
+      case 'p':
+        flag_p = flag_q = true;
+        break;
+      case 'q':
+        flag_q = true;
+        break;
+      case 'v':
+        flag_v = true;
+        break;
+      case 'x':
+        saw_x = true;
+        break;
+      case 1:
+        // -x swallows all following arguments, so we use '-' in the getopt
+        // string and collect files here.
+        if (!archive_name) {
+          archive_name = optarg;
+        } else if (saw_x) {
+          excludes.insert(optarg);
+        } else {
+          includes.insert(optarg);
+        }
+        break;
+      default:
+        ShowHelp(false);
     }
   }
 
-  if (!archive_name) die(0, "missing archive filename");
+  if (!archive_name) error(1, 0, "missing archive filename");
 
   // We can't support "-" to unzip from stdin because libziparchive relies on mmap.
   ZipArchiveHandle zah;
   int32_t err;
   if ((err = OpenArchive(archive_name, &zah)) != 0) {
-    die(0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
+    error(1, 0, "couldn't open %s: %s", archive_name, ErrorCodeString(err));
   }
 
   // Implement -d by changing into that directory.
   // We'll create implicit directories based on paths in the zip file, but we
   // require that the -d directory already exists.
-  if (flag_d && chdir(flag_d) == -1) die(errno, "couldn't chdir to %s", flag_d);
+  if (flag_d && chdir(flag_d) == -1) error(1, errno, "couldn't chdir to %s", flag_d);
 
   ProcessAll(zah);
 
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index ef29188..0710d0a 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -47,7 +47,6 @@
 #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 "zlib.h"
@@ -102,8 +101,38 @@
   return val;
 }
 
-static uint32_t ComputeHash(std::string_view name) {
-  return static_cast<uint32_t>(std::hash<std::string_view>{}(name));
+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 bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
+                             const ZipStringOffset& zip_string_offset) {
+  const ZipString from_offset = zip_string_offset.GetZipString(start);
+  return from_offset == zip_string;
+}
+
+/**
+ * Returns offset of ZipString#name from the start of the central directory in the memory map.
+ * For valid ZipStrings contained in the zip archive mmap, 0 < offset < 0xffffff.
+ */
+static inline uint32_t GetOffset(const uint8_t* name, const uint8_t* start) {
+  CHECK_GT(name, start);
+  CHECK_LT(name, start + 0xffffff);
+  return static_cast<uint32_t>(name - start);
 }
 
 /*
@@ -111,27 +140,27 @@
  * valid range.
  */
 static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
-                            std::string_view name, const uint8_t* start) {
+                            const ZipString& 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_offset != 0) {
-    if (hash_table[ent].ToStringView(start) == name) {
+    if (isZipStringEqual(start, name, hash_table[ent])) {
       return ent;
     }
     ent = (ent + 1) & (hash_table_size - 1);
   }
 
-  ALOGV("Zip: Unable to find entry %.*s", static_cast<int>(name.size()), name.data());
+  ALOGV("Zip: Unable to find entry %.*s", name.name_length, name.name);
   return kEntryNotFound;
 }
 
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipStringOffset* hash_table, const uint32_t hash_table_size,
-                         std::string_view name, const uint8_t* start) {
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint64_t hash_table_size,
+                         const ZipString& name, const uint8_t* start) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
 
@@ -140,18 +169,15 @@
    * Further, we guarantee that the hashtable size is not 0.
    */
   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());
+    if (isZipStringEqual(start, name, hash_table[ent])) {
+      // We've found a duplicate entry. We don't accept it
+      ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
       return kDuplicateEntry;
     }
     ent = (ent + 1) & (hash_table_size - 1);
   }
-
-  // `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());
+  hash_table[ent].name_offset = GetOffset(name.name, start);
+  hash_table[ent].name_length = name.name_length;
   return 0;
 }
 
@@ -178,7 +204,7 @@
 #endif
 }
 
-ZipArchive::ZipArchive(const void* address, size_t length)
+ZipArchive::ZipArchive(void* address, size_t length)
     : mapped_zip(address, length),
       close_file(false),
       directory_offset(0),
@@ -201,7 +227,7 @@
 }
 
 static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
-                                    off64_t file_length, uint32_t read_amount,
+                                    off64_t file_length, off64_t read_amount,
                                     uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
 
@@ -217,8 +243,7 @@
    * doing an initial minimal read; if we don't find it, retry with a
    * second read as above.)
    */
-  CHECK_LE(read_amount, std::numeric_limits<int32_t>::max());
-  int32_t i = read_amount - sizeof(EocdRecord);
+  int 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]);
@@ -321,9 +346,9 @@
    *
    * We start by pulling in the last part of the file.
    */
-  uint32_t read_amount = kMaxEOCDSearch;
+  off64_t read_amount = kMaxEOCDSearch;
   if (file_length < read_amount) {
-    read_amount = static_cast<uint32_t>(file_length);
+    read_amount = file_length;
   }
 
   std::vector<uint8_t> scan_buffer(read_amount);
@@ -353,7 +378,7 @@
       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(ZipStringOffset));
+          archive->hash_table_size, sizeof(ZipString));
     return -1;
   }
 
@@ -391,19 +416,21 @@
     const uint8_t* file_name = ptr + sizeof(CentralDirectoryRecord);
 
     if (file_name + file_name_length > cd_end) {
-      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);
+      ALOGW(
+          "Zip: file name boundary exceeds the central directory range, file_name_length: "
+          "%" PRIx16 ", cd_length: %zu",
+          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.
-    std::string_view entry_name{reinterpret_cast<const char*>(file_name), file_name_length};
+    /* 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,
                                      archive->central_directory.GetBasePtr());
     if (add_result != 0) {
@@ -471,20 +498,13 @@
   return OpenArchiveInternal(archive, fileName);
 }
 
-int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debug_file_name,
+int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
                               ZipArchiveHandle* handle) {
   ZipArchive* archive = new ZipArchive(address, length);
   *handle = archive;
   return OpenArchiveInternal(archive, debug_file_name);
 }
 
-ZipArchiveInfo GetArchiveInfo(ZipArchiveHandle archive) {
-  ZipArchiveInfo result;
-  result.archive_size = archive->mapped_zip.GetFileLength();
-  result.entry_count = archive->num_entries;
-  return result;
-}
-
 /*
  * Close a ZipArchive, closing the file and freeing the contents.
  */
@@ -525,19 +545,21 @@
   return 0;
 }
 
-static int32_t FindEntry(const ZipArchive* archive, const int32_t ent, ZipEntry* data) {
+static int32_t FindEntry(const ZipArchive* archive, const int 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* base_ptr = archive->central_directory.GetBasePtr();
-  const uint8_t* ptr = base_ptr + archive->hash_table[ent].name_offset;
+  const ZipString from_offset =
+      archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+  const uint8_t* ptr = from_offset.name;
   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;
@@ -621,41 +643,34 @@
   }
 
   // 4.4.2.1: the upper byte of `version_made_by` gives the source OS. Unix is 3.
-  data->version_made_by = cdr->version_made_by;
-  data->external_file_attributes = cdr->external_file_attributes;
-  if ((data->version_made_by >> 8) == 3) {
+  if ((cdr->version_made_by >> 8) == 3) {
     data->unix_mode = (cdr->external_file_attributes >> 16) & 0xffff;
   } else {
     data->unix_mode = 0777;
   }
 
-  // 4.4.4: general purpose bit flags.
-  data->gpbf = lfh->gpb_flags;
-
-  // 4.4.14: the lowest bit of the internal file attributes field indicates text.
-  // Currently only needed to implement zipinfo.
-  data->is_text = (cdr->internal_file_attributes & 1);
-
   // Check that the local file header name matches the declared
   // name in the 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");
+  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;
+    }
+    const ZipString from_offset =
+        archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+    if (memcmp(from_offset.name, name_buf.data(), nameLen)) {
+      return kInconsistentInformation;
+    }
+
+  } else {
+    ALOGW("Zip: lfh name did not match central directory.");
     return kInconsistentInformation;
   }
 
@@ -686,32 +701,52 @@
 }
 
 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;
 
-  std::string prefix;
-  std::string suffix;
+  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;
+    }
+  }
 
-  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) {}
+  ~IterationHandle() {
+    delete[] prefix.name;
+    delete[] suffix.name;
+  }
 };
 
 int32_t StartIteration(ZipArchiveHandle archive, void** cookie_ptr,
-                       const std::string_view optional_prefix,
-                       const std::string_view optional_suffix) {
+                       const ZipString* optional_prefix, const ZipString* optional_suffix) {
   if (archive == NULL || archive->hash_table == NULL) {
     ALOGW("Zip: Invalid ZipArchiveHandle");
     return kInvalidHandle;
   }
 
-  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;
-  }
+  IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
+  cookie->position = 0;
+  cookie->archive = archive;
 
-  *cookie_ptr = new IterationHandle(archive, optional_prefix, optional_suffix);
+  *cookie_ptr = cookie;
   return 0;
 }
 
@@ -719,33 +754,22 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-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());
+int32_t FindEntry(const ZipArchiveHandle archive, const ZipString& entryName, ZipEntry* data) {
+  if (entryName.name_length == 0) {
+    ALOGW("Zip: Invalid filename %.*s", entryName.name_length, entryName.name);
     return kInvalidEntryName;
   }
 
   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", static_cast<int>(entryName.size()), entryName.data());
-    return static_cast<int32_t>(ent);  // kEntryNotFound is safe to truncate.
+    ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
+    return ent;
   }
-  // We know there are at most hash_table_size entries, safe to truncate.
-  return FindEntry(archive, static_cast<uint32_t>(ent), data);
+  return FindEntry(archive, ent, data);
 }
 
-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) {
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
     ALOGW("Zip: Null ZipArchiveHandle");
@@ -762,14 +786,16 @@
   const uint32_t hash_table_length = archive->hash_table_size;
   const ZipStringOffset* hash_table = archive->hash_table;
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
-    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))) {
+    const ZipString from_offset =
+        hash_table[i].GetZipString(archive->central_directory.GetBasePtr());
+    if (hash_table[i].name_offset != 0 &&
+        (handle->prefix.name_length == 0 || from_offset.StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 || from_offset.EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
-      if (!error && name) {
-        *name = entry_name;
+      if (!error) {
+        name->name = from_offset.name;
+        name->name_length = hash_table[i].name_length;
       }
       return error;
     }
@@ -823,6 +849,7 @@
       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
@@ -834,7 +861,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.
-      long result = TEMP_FAILURE_RETRY(fallocate(fd, 0, current_offset, declared_length));
+      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),
@@ -852,7 +879,7 @@
 
     // Block device doesn't support ftruncate(2).
     if (!S_ISBLK(sb.st_mode)) {
-      long result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+      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));
@@ -971,16 +998,16 @@
   std::unique_ptr<z_stream, decltype(zstream_deleter)> zstream_guard(&zstream, zstream_deleter);
 
   const bool compute_crc = (crc_out != nullptr);
-  uLong crc = 0;
+  uint64_t crc = 0;
   uint32_t remaining_bytes = compressed_length;
   do {
     /* read as much as we can */
     if (zstream.avail_in == 0) {
-      const uint32_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
+      const size_t read_size = (remaining_bytes > kBufSize) ? kBufSize : remaining_bytes;
       const uint32_t offset = (compressed_length - remaining_bytes);
       // Make sure to read at offset to ensure concurrent access to the fd.
       if (!reader.ReadAtOffset(read_buf.data(), read_size, offset)) {
-        ALOGW("Zip: inflate read failed, getSize = %u: %s", read_size, strerror(errno));
+        ALOGW("Zip: inflate read failed, getSize = %zu: %s", read_size, strerror(errno));
         return kIoError;
       }
 
@@ -1004,8 +1031,7 @@
       if (!writer->Append(&write_buf[0], write_size)) {
         return kIoError;
       } else if (compute_crc) {
-        DCHECK_LE(write_size, kBufSize);
-        crc = crc32(crc, &write_buf[0], static_cast<uint32_t>(write_size));
+        crc = crc32(crc, &write_buf[0], write_size);
       }
 
       zstream.next_out = &write_buf[0];
@@ -1050,17 +1076,17 @@
 
   const uint32_t length = entry->uncompressed_length;
   uint32_t count = 0;
-  uLong crc = 0;
+  uint64_t crc = 0;
   while (count < length) {
     uint32_t remaining = length - count;
     off64_t offset = entry->offset + count;
 
     // Safe conversion because kBufSize is narrow enough for a 32 bit signed value.
-    const uint32_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+    const size_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 = %u, offset = %" PRId64 ": %s",
+      ALOGW("CopyFileToFile: copy read failed, block_size = %zu, offset = %" PRId64 ": %s",
             block_size, static_cast<int64_t>(offset), strerror(errno));
       return kIoError;
     }
@@ -1137,6 +1163,12 @@
   return archive->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);
+}
+
 #if !defined(_WIN32)
 class ProcessWriter : public zip_archive::Writer {
  public:
@@ -1168,7 +1200,7 @@
   return fd_;
 }
 
-const void* MappedZipFile::GetBasePtr() const {
+void* MappedZipFile::GetBasePtr() const {
   if (has_fd_) {
     ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
     return nullptr;
@@ -1204,14 +1236,13 @@
       ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", off, data_length_);
       return false;
     }
-    memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
+    memcpy(buf, static_cast<uint8_t*>(base_ptr_) + off, len);
   }
   return true;
 }
 
-void CentralDirectory::Initialize(const void* map_base_ptr, off64_t cd_start_offset,
-                                  size_t cd_size) {
-  base_ptr_ = static_cast<const uint8_t*>(map_base_ptr) + cd_start_offset;
+void CentralDirectory::Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size) {
+  base_ptr_ = static_cast<uint8_t*>(map_base_ptr) + cd_start_offset;
   length_ = cd_size;
 }
 
diff --git a/libziparchive/zip_archive_benchmark.cpp b/libziparchive/zip_archive_benchmark.cpp
index 09d3b8a..46aa5a6 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.
-  std::string_view name("thisFileNameDoesNotExist");
+  ZipString name("thisFileNameDoesNotExist");
 
   // Start the benchmark.
-  for (auto _ : state) {
+  while (state.KeepRunning()) {
     OpenArchive(temp_file->path, &handle);
     FindEntry(handle, name, &data);
     CloseArchive(handle);
@@ -71,11 +71,11 @@
   ZipArchiveHandle handle;
   void* iteration_cookie;
   ZipEntry data;
-  std::string name;
+  ZipString name;
 
-  for (auto _ : state) {
+  while (state.KeepRunning()) {
     OpenArchive(temp_file->path, &handle);
-    StartIteration(handle, &iteration_cookie);
+    StartIteration(handle, &iteration_cookie, nullptr, nullptr);
     while (Next(iteration_cookie, &data, &name) == 0) {
     }
     EndIteration(iteration_cookie);
@@ -84,27 +84,4 @@
 }
 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 60fdec0..330a02a 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -95,14 +95,14 @@
   explicit MappedZipFile(const int fd)
       : has_fd_(true), fd_(fd), base_ptr_(nullptr), data_length_(0) {}
 
-  explicit MappedZipFile(const void* address, size_t length)
+  explicit MappedZipFile(void* address, size_t length)
       : has_fd_(false), fd_(-1), base_ptr_(address), data_length_(static_cast<off64_t>(length)) {}
 
   bool HasFd() const { return has_fd_; }
 
   int GetFileDescriptor() const;
 
-  const void* GetBasePtr() const;
+  void* GetBasePtr() const;
 
   off64_t GetFileLength() const;
 
@@ -117,7 +117,7 @@
 
   const int fd_;
 
-  const void* const base_ptr_;
+  void* const base_ptr_;
   const off64_t data_length_;
 };
 
@@ -129,7 +129,7 @@
 
   size_t GetMapLength() const { return length_; }
 
-  void Initialize(const void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
+  void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
 
  private:
   const uint8_t* base_ptr_;
@@ -137,22 +137,22 @@
 };
 
 /**
- * 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.
+ * More space efficient string representation of strings in an mmaped zipped file than
+ * std::string_view or ZipString. Using ZipString as an entry in the ZipArchive hashtable wastes
+ * space. ZipString 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};
+  const ZipString GetZipString(const uint8_t* start) const {
+    ZipString zip_string;
+    zip_string.name = start + name_offset;
+    zip_string.name_length = name_length;
+    return zip_string;
   }
 };
 
@@ -177,7 +177,7 @@
   ZipStringOffset* hash_table;
 
   ZipArchive(const int fd, bool assume_ownership);
-  ZipArchive(const void* address, size_t length);
+  ZipArchive(void* address, size_t length);
   ~ZipArchive();
 
   bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
diff --git a/libziparchive/zip_archive_stream_entry.cc b/libziparchive/zip_archive_stream_entry.cc
index 1ec95b6..9ec89b1 100644
--- a/libziparchive/zip_archive_stream_entry.cc
+++ b/libziparchive/zip_archive_stream_entry.cc
@@ -27,7 +27,6 @@
 #include <vector>
 
 #include <android-base/file.h>
-#include <android-base/logging.h>
 #include <log/log.h>
 
 #include <ziparchive/zip_archive.h>
@@ -78,12 +77,6 @@
 }
 
 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;
   }
@@ -104,8 +97,7 @@
   if (bytes < data_.size()) {
     data_.resize(bytes);
   }
-  computed_crc32_ = static_cast<uint32_t>(
-      crc32(computed_crc32_, data_.data(), static_cast<uint32_t>(data_.size())));
+  computed_crc32_ = crc32(computed_crc32_, data_.data(), data_.size());
   length_ -= bytes;
   offset_ += bytes;
   return &data_;
@@ -200,15 +192,9 @@
 }
 
 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 = static_cast<uint32_t>(out_.size());
+    z_stream_.avail_out = out_.size();
     ;
   }
 
@@ -217,9 +203,7 @@
       if (compressed_length_ == 0) {
         return nullptr;
       }
-      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_;
+      size_t bytes = (compressed_length_ > in_.size()) ? in_.size() : compressed_length_;
       ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle_);
       errno = 0;
       if (!archive->mapped_zip.ReadAtOffset(in_.data(), bytes, offset_)) {
@@ -246,16 +230,14 @@
 
     if (z_stream_.avail_out == 0) {
       uncompressed_length_ -= out_.size();
-      computed_crc32_ = static_cast<uint32_t>(
-          crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
+      computed_crc32_ = crc32(computed_crc32_, out_.data(), 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_ = static_cast<uint32_t>(
-            crc32(computed_crc32_, out_.data(), static_cast<uint32_t>(out_.size())));
+        computed_crc32_ = crc32(computed_crc32_, out_.data(), out_.size());
         uncompressed_length_ -= out_.size();
         return &out_;
       }
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 8781ab7..cea42d4 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -27,7 +27,6 @@
 #include <vector>
 
 #include <android-base/file.h>
-#include <android-base/logging.h>
 #include <android-base/mapped_file.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
@@ -64,6 +63,11 @@
   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));
@@ -107,27 +111,7 @@
   close(fd);
 }
 
-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,
+static void AssertIterationOrder(const ZipString* prefix, const ZipString* suffix,
                                  const std::vector<std::string>& expected_names_sorted) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -138,10 +122,10 @@
   ZipEntry data;
   std::vector<std::string> names;
 
-  std::string name;
+  ZipString name;
   for (size_t i = 0; i < expected_names_sorted.size(); ++i) {
     ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
-    names.push_back(name);
+    names.push_back(std::string(reinterpret_cast<const char*>(name.name), name.name_length));
   }
 
   // End of iteration.
@@ -157,26 +141,30 @@
   static const std::vector<std::string> kExpectedMatchesSorted = {"a.txt", "b.txt", "b/", "b/c.txt",
                                                                   "b/d.txt"};
 
-  AssertIterationOrder("", "", kExpectedMatchesSorted);
+  AssertIterationOrder(nullptr, nullptr, kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithPrefix) {
+  ZipString prefix("b/");
   static const std::vector<std::string> kExpectedMatchesSorted = {"b/", "b/c.txt", "b/d.txt"};
 
-  AssertIterationOrder("b/", "", kExpectedMatchesSorted);
+  AssertIterationOrder(&prefix, nullptr, 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("", ".txt", kExpectedMatchesSorted);
+  AssertIterationOrder(nullptr, &suffix, 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("b", ".txt", kExpectedMatchesSorted);
+  AssertIterationOrder(&prefix, &suffix, kExpectedMatchesSorted);
 }
 
 TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
@@ -184,10 +172,12 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, "x", "y"));
+  ZipString prefix("x");
+  ZipString suffix("y");
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
 
   ZipEntry data;
-  std::string name;
+  ZipString name;
 
   // End of iteration.
   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
@@ -200,7 +190,9 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
+  ZipString name;
+  SetZipString(&name, kATxtName);
+  ASSERT_EQ(0, FindEntry(handle, name, &data));
 
   // Known facts about a.txt, from zipinfo -v.
   ASSERT_EQ(63, data.offset);
@@ -211,28 +203,9 @@
   ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
 
   // An entry that doesn't exist. Should be a negative return code.
-  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));
+  ZipString absent_name;
+  SetZipString(&absent_name, kNonexistentTxtName);
+  ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
 
   CloseArchive(handle);
 }
@@ -242,9 +215,9 @@
   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, nullptr, nullptr));
 
-  std::string name;
+  ZipString name;
   ZipEntry data;
 
   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
@@ -259,7 +232,9 @@
 
   // An entry that's deflated.
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, kATxtName, &data));
+  ZipString a_name;
+  SetZipString(&a_name, kATxtName);
+  ASSERT_EQ(0, FindEntry(handle, a_name, &data));
   const uint32_t a_size = data.uncompressed_length;
   ASSERT_EQ(a_size, kATxtContents.size());
   uint8_t* buffer = new uint8_t[a_size];
@@ -268,7 +243,9 @@
   delete[] buffer;
 
   // An entry that's stored.
-  ASSERT_EQ(0, FindEntry(handle, kBTxtName, &data));
+  ZipString b_name;
+  SetZipString(&b_name, kBTxtName);
+  ASSERT_EQ(0, FindEntry(handle, b_name, &data));
   const uint32_t b_size = data.uncompressed_length;
   ASSERT_EQ(b_size, kBTxtContents.size());
   buffer = new uint8_t[b_size];
@@ -323,7 +300,9 @@
   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle, false));
 
   ZipEntry entry;
-  ASSERT_EQ(0, FindEntry(handle, kEmptyTxtName, &entry));
+  ZipString empty_name;
+  SetZipString(&empty_name, kEmptyTxtName);
+  ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
   ASSERT_EQ(static_cast<uint32_t>(0), entry.uncompressed_length);
   uint8_t buffer[1];
   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
@@ -346,12 +325,14 @@
   ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle, false));
 
   ZipEntry entry;
-  ASSERT_EQ(0, FindEntry(handle, kAbTxtName, &entry));
+  ZipString ab_name;
+  SetZipString(&ab_name, kAbTxtName);
+  ASSERT_EQ(0, FindEntry(handle, ab_name, &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], static_cast<uint32_t>(buffer.size())));
+  ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
 
   // Extract the entry to a file.
   TemporaryFile tmp_output_file;
@@ -403,7 +384,9 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry entry;
-  ASSERT_EQ(0, FindEntry(handle, kATxtName, &entry));
+  ZipString name;
+  SetZipString(&name, kATxtName);
+  ASSERT_EQ(0, FindEntry(handle, name, &entry));
   ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
 
   // Assert that the first 8 bytes of the file haven't been clobbered.
@@ -432,15 +415,16 @@
   ASSERT_EQ(0, fstat(fd, &sb));
 
   // Memory map the file first and open the archive from the memory region.
-  auto file_map{
-      android::base::MappedFile::FromFd(fd, 0, static_cast<size_t>(sb.st_size), PROT_READ)};
+  auto file_map{android::base::MappedFile::FromFd(fd, 0, sb.st_size, PROT_READ)};
   ZipArchiveHandle 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, "META-INF/com/google/android/update-binary", &binary_entry));
+  ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
   TemporaryFile tmp_binary;
   ASSERT_NE(-1, tmp_binary.fd);
   ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
@@ -449,7 +433,9 @@
 
 static void ZipArchiveStreamTest(ZipArchiveHandle& handle, const std::string& entry_name, bool raw,
                                  bool verified, ZipEntry* entry, std::vector<uint8_t>* read_data) {
-  ASSERT_EQ(0, FindEntry(handle, entry_name, entry));
+  ZipString name;
+  SetZipString(&name, entry_name);
+  ASSERT_EQ(0, FindEntry(handle, name, entry));
   std::unique_ptr<ZipArchiveStreamEntry> stream;
   if (raw) {
     stream.reset(ZipArchiveStreamEntry::CreateRaw(handle, *entry));
@@ -502,8 +488,7 @@
 
   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(), static_cast<uint32_t>(cmp_data.size())));
+  ASSERT_EQ(0, ExtractToMemory(handle, &entry, cmp_data.data(), cmp_data.size()));
   ASSERT_TRUE(memcmp(read_data.data(), cmp_data.data(), read_data.size()) == 0);
 
   CloseArchive(handle);
@@ -600,7 +585,11 @@
   // an entry whose name is "name" and whose size is 12 (contents =
   // "abdcdefghijk").
   ZipEntry entry;
-  ASSERT_EQ(0, FindEntry(handle, "name", &entry));
+  ZipString name;
+  std::string name_str = "name";
+  SetZipString(&name, name_str);
+
+  ASSERT_EQ(0, FindEntry(handle, name, &entry));
   ASSERT_EQ(static_cast<uint32_t>(12), entry.uncompressed_length);
 
   entry_out->resize(12);
@@ -748,8 +737,8 @@
 };
 
 TEST(ziparchive, Inflate) {
-  const uint32_t compressed_length = static_cast<uint32_t>(kATxtContentsCompressed.size());
-  const uint32_t uncompressed_length = static_cast<uint32_t>(kATxtContents.size());
+  const uint32_t compressed_length = kATxtContentsCompressed.size();
+  const uint32_t uncompressed_length = kATxtContents.size();
 
   const VectorReader reader(kATxtContentsCompressed);
   {
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 67279a6..0df0fa5 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -130,7 +130,7 @@
   return error_code;
 }
 
-int32_t ZipWriter::StartEntry(std::string_view path, size_t flags) {
+int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
   uint32_t alignment = 0;
   if (flags & kAlign32) {
     flags &= ~kAlign32;
@@ -139,11 +139,11 @@
   return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
 }
 
-int32_t ZipWriter::StartAlignedEntry(std::string_view path, size_t flags, uint32_t alignment) {
+int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
   return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
 }
 
-int32_t ZipWriter::StartEntryWithTime(std::string_view path, size_t flags, time_t time) {
+int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
   uint32_t alignment = 0;
   if (flags & kAlign32) {
     flags &= ~kAlign32;
@@ -169,8 +169,8 @@
     year = 80;
   }
 
-  *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);
+  *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;
 }
 
 static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
@@ -193,22 +193,16 @@
   dst->compression_method = src.compression_method;
   dst->last_mod_time = src.last_mod_time;
   dst->last_mod_date = src.last_mod_date;
-  DCHECK_LE(src.path.size(), std::numeric_limits<uint16_t>::max());
-  dst->file_name_length = static_cast<uint16_t>(src.path.size());
+  dst->file_name_length = src.path.size();
   dst->extra_field_length = src.padding_length;
 }
 
-int32_t ZipWriter::StartAlignedEntryWithTime(std::string_view path, size_t flags, time_t time,
+int32_t ZipWriter::StartAlignedEntryWithTime(const char* 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;
   }
@@ -216,17 +210,10 @@
   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())) {
@@ -247,24 +234,13 @@
   ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
 
   off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
-  // 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;
-
+  std::vector<char> zero_padding;
   if (alignment != 0 && (offset & (alignment - 1))) {
     // Pad the extra field so the data will be aligned.
-    uint16_t padding = static_cast<uint16_t>(alignment - (offset % alignment));
+    uint16_t padding = alignment - (offset % alignment);
     file_entry.padding_length = padding;
     offset += padding;
-    if (padding <= std::size(kSmallZeroPadding)) {
-        zero_padding = kSmallZeroPadding;
-    } else {
-        zero_padding_big.resize(padding, 0);
-        zero_padding = zero_padding_big.data();
-    }
+    zero_padding.resize(padding, 0);
   }
 
   LocalFileHeader header = {};
@@ -276,11 +252,11 @@
     return HandleError(kIoError);
   }
 
-  if (fwrite(path.data(), 1, path.size(), file_) != path.size()) {
+  if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
     return HandleError(kIoError);
   }
 
-  if (file_entry.padding_length != 0 && fwrite(zero_padding, 1, file_entry.padding_length,
+  if (file_entry.padding_length != 0 && fwrite(zero_padding.data(), 1, file_entry.padding_length,
                                                file_) != file_entry.padding_length) {
     return HandleError(kIoError);
   }
@@ -338,8 +314,7 @@
   }
 
   z_stream_->next_out = buffer_.data();
-  DCHECK_EQ(buffer_.size(), kBufSize);
-  z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
+  z_stream_->avail_out = buffer_.size();
   return kNoError;
 }
 
@@ -347,31 +322,25 @@
   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, len32);
+    result = CompressBytes(&current_file_entry_, data, len);
   } else {
-    result = StoreBytes(&current_file_entry_, data, len32);
+    result = StoreBytes(&current_file_entry_, data, len);
   }
 
   if (result != kNoError) {
     return result;
   }
 
-  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;
+  current_file_entry_.crc32 =
+      crc32(current_file_entry_.crc32, reinterpret_cast<const Bytef*>(data), len);
+  current_file_entry_.uncompressed_size += len;
   return kNoError;
 }
 
-int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, uint32_t len) {
+int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, size_t len) {
   CHECK(state_ == State::kWritingEntry);
 
   if (fwrite(data, 1, len, file_) != len) {
@@ -382,7 +351,7 @@
   return kNoError;
 }
 
-int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, uint32_t len) {
+int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, size_t len) {
   CHECK(state_ == State::kWritingEntry);
   CHECK(z_stream_);
   CHECK(z_stream_->next_out != nullptr);
@@ -410,8 +379,7 @@
 
       // Reset the output buffer for the next input.
       z_stream_->next_out = buffer_.data();
-      DCHECK_EQ(buffer_.size(), kBufSize);
-      z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
+      z_stream_->avail_out = buffer_.size();
     }
   }
   return kNoError;
@@ -436,8 +404,7 @@
     current_offset_ += write_bytes;
 
     z_stream_->next_out = buffer_.data();
-    DCHECK_EQ(buffer_.size(), kBufSize);
-    z_stream_->avail_out = static_cast<uint32_t>(buffer_.size());
+    z_stream_->avail_out = buffer_.size();
   }
   if (zerr != Z_STREAM_END) {
     return HandleError(kZlibError);
@@ -455,11 +422,6 @@
   return kNoError;
 }
 
-bool ZipWriter::ShouldUseDataDescriptor() const {
-  // Only use a trailing "data descriptor" if the output isn't seekable.
-  return !seekable_;
-}
-
 int32_t ZipWriter::FinishEntry() {
   if (state_ != State::kWritingEntry) {
     return kInvalidState;
@@ -472,7 +434,7 @@
     }
   }
 
-  if (ShouldUseDataDescriptor()) {
+  if ((current_file_entry_.compression_method & kCompressDeflated) || !seekable_) {
     // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
     // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
     const uint32_t sig = DataDescriptor::kOptSignature;
@@ -520,7 +482,7 @@
   for (FileEntry& file : files_) {
     CentralDirectoryRecord cdr = {};
     cdr.record_signature = CentralDirectoryRecord::kSignature;
-    if (ShouldUseDataDescriptor()) {
+    if ((file.compression_method & kCompressDeflated) || !seekable_) {
       cdr.gpb_flags |= kGPBDDFlagMask;
     }
     cdr.compression_method = file.compression_method;
@@ -529,11 +491,7 @@
     cdr.crc32 = file.crc32;
     cdr.compressed_size = file.compressed_size;
     cdr.uncompressed_size = file.uncompressed_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.file_name_length = file.path.size();
     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);
@@ -550,15 +508,10 @@
   er.eocd_signature = EocdRecord::kSignature;
   er.disk_num = 0;
   er.cd_start_disk = 0;
-  // 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);
+  er.num_records_on_disk = files_.size();
+  er.num_records = files_.size();
+  er.cd_size = current_offset_ - startOfCdr;
+  er.cd_start_offset = 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 d324d4b..c284273 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, "file.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("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, "file.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("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, "file/file.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("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, "file/file2.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("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, "align.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("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, "align.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("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, "align.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("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, "align.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
   EXPECT_EQ(0, data.offset & 0xfff);
 
   struct tm mod = data.GetModificationTime();
@@ -241,9 +241,8 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
   EXPECT_EQ(kCompressDeflated, data.method);
-  EXPECT_EQ(0u, data.has_data_descriptor);
   ASSERT_EQ(4u, data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
 
@@ -258,7 +257,7 @@
   std::vector<uint8_t> buffer(kBufSize);
   size_t prev = 1;
   for (size_t i = 0; i < kBufSize; i++) {
-    buffer[i] = static_cast<uint8_t>(i + prev);
+    buffer[i] = i + prev;
     prev = i;
   }
 
@@ -274,14 +273,13 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("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(),
-                               static_cast<uint32_t>(decompress.size())));
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
   EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
       << "Input buffer and output buffer are different.";
 
@@ -341,40 +339,17 @@
   ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "keep.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
   ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
 
-  ASSERT_NE(0, FindEntry(handle, "drop.txt", &data));
+  ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
 
-  ASSERT_EQ(0, FindEntry(handle, "replace.txt", &data));
+  ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
   ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
 
   CloseArchive(handle);
 }
 
-TEST_F(zipwriter, WriteToUnseekableFile) {
-  const char* expected = "hello";
-  ZipWriter writer(file_);
-  writer.seekable_ = false;
-
-  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
-  ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
-  ASSERT_EQ(0, writer.FinishEntry());
-  ASSERT_EQ(0, writer.Finish());
-  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
-
-  ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
-  ZipEntry data;
-  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
-  EXPECT_EQ(kCompressStored, data.method);
-  EXPECT_EQ(1u, data.has_data_descriptor);
-  EXPECT_EQ(strlen(expected), data.compressed_length);
-  ASSERT_EQ(strlen(expected), data.uncompressed_length);
-  ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
-  CloseArchive(handle);
-}
-
 TEST_F(zipwriter, TruncateFileAfterBackup) {
   ZipWriter writer(file_);
 
@@ -416,7 +391,7 @@
   actual.resize(expected.size());
 
   uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
-  if (ExtractToMemory(handle, zip_entry, buffer, static_cast<uint32_t>(actual.size())) != 0) {
+  if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) {
     return ::testing::AssertionFailure() << "failed to extract entry";
   }
 
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index deebb8e..4c5ca03 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -1,15 +1,3 @@
-cc_defaults {
-    name: "stats_defaults",
-
-    product_variables: {
-        use_lmkd_stats_log: {
-            cflags: [
-                "-DLMKD_LOG_STATS"
-            ],
-        },
-    },
-}
-
 cc_binary {
     name: "lmkd",
 
@@ -27,7 +15,13 @@
     local_include_dirs: ["include"],
     cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
     init_rc: ["lmkd.rc"],
-    defaults: ["stats_defaults"],
+    product_variables: {
+        use_lmkd_stats_log: {
+            cflags: [
+                "-DLMKD_LOG_STATS"
+            ],
+        },
+    },
     logtags: ["event.logtags"],
 }
 
@@ -38,7 +32,6 @@
         "-Wall",
         "-Werror",
     ],
-    defaults: ["stats_defaults"],
     shared_libs: [
         "liblog",
     ],
@@ -48,7 +41,6 @@
 cc_library_static {
     name: "liblmkd_utils",
     srcs: ["liblmkd_utils.c"],
-    recovery_available: true,
     shared_libs: [
         "libcutils",
     ],
diff --git a/lmkd/README.md b/lmkd/README.md
index 8a73692..656a6ea 100644
--- a/lmkd/README.md
+++ b/lmkd/README.md
@@ -60,31 +60,6 @@
                              any eligible task (fast decision). Default = false
 
   ro.lmk.kill_timeout_ms:    duration in ms after a kill when no additional
-                             kill will be done. Default = 0 (disabled)
+                             kill will be done, Default = 0 (disabled)
 
   ro.lmk.debug:              enable lmkd debug logs, Default = false
-
-  ro.lmk.swap_free_low_percentage: level of free swap as a percentage of the
-                             total swap space used as a threshold to consider
-                             the system as swap space starved. Default for
-                             low-RAM devices = 10, for high-end devices = 20
-
-  ro.lmk.thrashing_limit:    number of workingset refaults as a percentage of
-                             the file-backed pagecache size used as a threshold
-                             to consider system thrashing its pagecache.
-                             Default for low-RAM devices = 30, for high-end
-                             devices = 100
-
-  ro.lmk.thrashing_limit_decay: thrashing threshold decay expressed as a
-                             percentage of the original threshold used to lower
-                             the threshold when system does not recover even
-                             after a kill. Default for low-RAM devices = 50,
-                             for high-end devices = 10
-
-  ro.lmk.psi_partial_stall_ms: partial PSI stall threshold in milliseconds for
-                             triggering low memory notification. Default for
-                             low-RAM devices = 200, for high-end devices = 70
-
-  ro.lmk.psi_complete_stall_ms: complete PSI stall threshold in milliseconds for
-                             triggering critical memory notification. Default =
-                             700
diff --git a/lmkd/event.logtags b/lmkd/event.logtags
index 452f411..065c6db 100644
--- a/lmkd/event.logtags
+++ b/lmkd/event.logtags
@@ -17,8 +17,8 @@
 # Multiple values are separated by commas.
 #
 # The data type is a number from the following values:
-# 1: int32_t
-# 2: int64_t
+# 1: int
+# 2: long
 # 3: string
 # 4: list
 #
@@ -34,5 +34,5 @@
 #
 # TODO: generate ".java" and ".h" files with integer constants from this file.
 
-# for killinfo logs
-10195355 killinfo (Pid|1|5),(Uid|1|5),(OomAdj|1),(MinOomAdj|1),(TaskSize|1),(enum kill_reasons|1|5),(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),(IonHeap|1),(IonHeapPool|1),(CmaFree|1)
+# 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/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h
index 92e4d41..72e3f4a 100644
--- a/lmkd/include/liblmkd_utils.h
+++ b/lmkd/include/liblmkd_utils.h
@@ -40,14 +40,6 @@
 int lmkd_register_proc(int sock, struct lmk_procprio *params);
 
 /*
- * Unregisters a process previously registered with lmkd.
- * On success returns 0.
- * On error, -1 is returned.
- * In the case of error errno is set appropriately.
- */
-int lmkd_unregister_proc(int sock, struct lmk_procremove *params);
-
-/*
  * Creates memcg directory for given process.
  * On success returns 0.
  * -1 is returned if path creation failed.
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
index bd9b80e..59377dd 100644
--- a/lmkd/include/lmkd.h
+++ b/lmkd/include/lmkd.h
@@ -87,33 +87,21 @@
     return idx * sizeof(int);
 }
 
-/* Process types for lmk_procprio.ptype */
-enum proc_type {
-    PROC_TYPE_FIRST,
-    PROC_TYPE_APP = PROC_TYPE_FIRST,
-    PROC_TYPE_SERVICE,
-    PROC_TYPE_COUNT,
-};
-
 /* LMK_PROCPRIO packet payload */
 struct lmk_procprio {
     pid_t pid;
     uid_t uid;
     int oomadj;
-    enum proc_type ptype;
 };
 
 /*
  * For LMK_PROCPRIO packet get its payload.
  * Warning: no checks performed, caller should ensure valid parameters.
  */
-static inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet, int field_count,
-                                          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]);
-    /* if field is missing assume PROC_TYPE_APP for backward compatibility */
-    params->ptype = field_count > 3 ? (enum proc_type)ntohl(packet[4]) : PROC_TYPE_APP;
 }
 
 /*
@@ -125,8 +113,7 @@
     packet[1] = htonl(params->pid);
     packet[2] = htonl(params->uid);
     packet[3] = htonl(params->oomadj);
-    packet[4] = htonl((int)params->ptype);
-    return 5 * sizeof(int);
+    return 4 * sizeof(int);
 }
 
 /* LMK_PROCREMOVE packet payload */
@@ -148,7 +135,7 @@
  * Warning: no checks performed, caller should ensure valid parameters.
  */
 static inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
-                                              struct lmk_procremove* params) {
+                                              struct lmk_procprio* params) {
     packet[0] = htonl(LMK_PROCREMOVE);
     packet[1] = htonl(params->pid);
     return 2 * sizeof(int);
diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c
index 280c149..fa3b7a9 100644
--- a/lmkd/liblmkd_utils.c
+++ b/lmkd/liblmkd_utils.c
@@ -28,7 +28,7 @@
 int lmkd_connect() {
     return socket_local_client("lmkd",
                                ANDROID_SOCKET_NAMESPACE_RESERVED,
-                               SOCK_SEQPACKET | SOCK_CLOEXEC);
+                               SOCK_SEQPACKET);
 }
 
 int lmkd_register_proc(int sock, struct lmk_procprio *params) {
@@ -42,17 +42,6 @@
     return (ret < 0) ? -1 : 0;
 }
 
-int lmkd_unregister_proc(int sock, struct lmk_procremove *params) {
-    LMKD_CTRL_PACKET packet;
-    size_t size;
-    int ret;
-
-    size = lmkd_pack_set_procremove(packet, params);
-    ret = TEMP_FAILURE_RETRY(write(sock, packet, size));
-
-    return (ret < 0) ? -1 : 0;
-}
-
 int create_memcg(uid_t uid, pid_t pid) {
     char buf[256];
     int tasks_file;
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 4352498..7c5a466 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -31,7 +31,6 @@
 #include <sys/mman.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
-#include <sys/syscall.h>
 #include <sys/sysinfo.h>
 #include <sys/time.h>
 #include <sys/types.h>
@@ -45,11 +44,12 @@
 #include <log/log.h>
 #include <log/log_event_list.h>
 #include <log/log_time.h>
-#include <private/android_filesystem_config.h>
 #include <psi/psi.h>
 #include <system/thread_defs.h>
 
+#ifdef LMKD_LOG_STATS
 #include "statslog.h"
+#endif
 
 /*
  * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
@@ -79,14 +79,11 @@
 #define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
 #define ZONEINFO_PATH "/proc/zoneinfo"
 #define MEMINFO_PATH "/proc/meminfo"
-#define VMSTAT_PATH "/proc/vmstat"
 #define PROC_STATUS_TGID_FIELD "Tgid:"
 #define LINE_MAX 128
 
-#define PERCEPTIBLE_APP_ADJ 200
-
 /* Android Logger event logtags (see event.logtags) */
-#define KILLINFO_LOG_TAG 10195355
+#define MEMINFO_LOG_TAG 10195355
 
 /* gid containing AID_SYSTEM required */
 #define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
@@ -113,43 +110,15 @@
  * PSI_WINDOW_SIZE_MS after the event happens.
  */
 #define PSI_WINDOW_SIZE_MS 1000
-/* Polling period after PSI signal when pressure is high */
-#define PSI_POLL_PERIOD_SHORT_MS 10
-/* Polling period after PSI signal when pressure is low */
-#define PSI_POLL_PERIOD_LONG_MS 100
+/* 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 max(a, b) (((a) > (b)) ? (a) : (b))
 
 #define FAIL_REPORT_RLIMIT_MS 1000
 
-/*
- * System property defaults
- */
-/* ro.lmk.swap_free_low_percentage property defaults */
-#define DEF_LOW_SWAP_LOWRAM 10
-#define DEF_LOW_SWAP 20
-/* ro.lmk.thrashing_limit property defaults */
-#define DEF_THRASHING_LOWRAM 30
-#define DEF_THRASHING 100
-/* ro.lmk.thrashing_limit_decay property defaults */
-#define DEF_THRASHING_DECAY_LOWRAM 50
-#define DEF_THRASHING_DECAY 10
-/* ro.lmk.psi_partial_stall_ms property defaults */
-#define DEF_PARTIAL_STALL_LOWRAM 200
-#define DEF_PARTIAL_STALL 70
-/* ro.lmk.psi_complete_stall_ms property defaults */
-#define DEF_COMPLETE_STALL 700
-
-static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
-    return syscall(__NR_pidfd_open, pid, flags);
-}
-
-static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
-                                        unsigned int flags) {
-    return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
-}
-
 /* default to old in-kernel interface if no memory pressure events */
 static bool use_inkernel_interface = true;
 static bool has_inkernel_module;
@@ -180,11 +149,6 @@
 
 static int level_oomadj[VMPRESS_LEVEL_COUNT];
 static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
-static bool pidfd_supported;
-static int last_kill_pid_or_fd = -1;
-static struct timespec last_kill_tm;
-
-/* lmkd configurable parameters */
 static bool debug_process_killing;
 static bool enable_pressure_upgrade;
 static int64_t upgrade_pressure;
@@ -195,12 +159,7 @@
 static bool use_minfree_levels;
 static bool per_app_memcg;
 static int swap_free_low_percentage;
-static int psi_partial_stall_ms;
-static int psi_complete_stall_ms;
-static int thrashing_limit_pct;
-static int thrashing_limit_decay_pct;
 static bool use_psi_monitors = false;
-static struct kernel_poll_info kpoll_info;
 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 */
@@ -209,44 +168,20 @@
 
 static android_log_context ctx;
 
-enum polling_update {
-    POLLING_DO_NOT_CHANGE,
-    POLLING_START,
-    POLLING_STOP,
-    POLLING_PAUSE,
-    POLLING_RESUME,
-};
-
-/*
- * Data used for periodic polling for the memory state of the device.
- * Note that when system is not polling poll_handler is set to NULL,
- * when polling starts poll_handler gets set and is reset back to
- * NULL when polling stops.
- */
-struct polling_params {
-    struct event_handler_info* poll_handler;
-    struct event_handler_info* paused_handler;
-    struct timespec poll_start_tm;
-    struct timespec last_poll_tm;
-    int polling_interval_ms;
-    enum polling_update update;
-};
-
 /* data required to handle events */
 struct event_handler_info {
     int data;
-    void (*handler)(int data, uint32_t events, struct polling_params *poll_params);
+    void (*handler)(int data, uint32_t events);
 };
 
 /* data required to handle socket events */
 struct sock_event_handler_info {
     int sock;
-    pid_t pid;
     struct event_handler_info handler_info;
 };
 
-/* max supported number of data connections (AMS, init, tests) */
-#define MAX_DATA_CONN 3
+/* max supported number of data connections */
+#define MAX_DATA_CONN 2
 
 /* socket event handler data */
 static struct sock_event_handler_info ctrl_sock;
@@ -255,11 +190,8 @@
 /* vmpressure event handler data */
 static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
 
-/*
- * 1 ctrl listen socket, 3 ctrl data socket, 3 memory pressure levels,
- * 1 lmk events + 1 fd to wait for process death
- */
-#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT + 1 + 1)
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
+#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
 static int epollfd;
 static int maxevents;
 
@@ -272,99 +204,37 @@
 static int lowmem_targets_size;
 
 /* Fields to parse in /proc/zoneinfo */
-/* zoneinfo per-zone fields */
-enum zoneinfo_zone_field {
-    ZI_ZONE_NR_FREE_PAGES = 0,
-    ZI_ZONE_MIN,
-    ZI_ZONE_LOW,
-    ZI_ZONE_HIGH,
-    ZI_ZONE_PRESENT,
-    ZI_ZONE_NR_FREE_CMA,
-    ZI_ZONE_FIELD_COUNT
+enum zoneinfo_field {
+    ZI_NR_FREE_PAGES = 0,
+    ZI_NR_FILE_PAGES,
+    ZI_NR_SHMEM,
+    ZI_NR_UNEVICTABLE,
+    ZI_WORKINGSET_REFAULT,
+    ZI_HIGH,
+    ZI_FIELD_COUNT
 };
 
-static const char* const zoneinfo_zone_field_names[ZI_ZONE_FIELD_COUNT] = {
+static const char* const zoneinfo_field_names[ZI_FIELD_COUNT] = {
     "nr_free_pages",
-    "min",
-    "low",
+    "nr_file_pages",
+    "nr_shmem",
+    "nr_unevictable",
+    "workingset_refault",
     "high",
-    "present",
-    "nr_free_cma",
 };
 
-/* zoneinfo per-zone special fields */
-enum zoneinfo_zone_spec_field {
-    ZI_ZONE_SPEC_PROTECTION = 0,
-    ZI_ZONE_SPEC_PAGESETS,
-    ZI_ZONE_SPEC_FIELD_COUNT,
-};
-
-static const char* const zoneinfo_zone_spec_field_names[ZI_ZONE_SPEC_FIELD_COUNT] = {
-    "protection:",
-    "pagesets",
-};
-
-/* see __MAX_NR_ZONES definition in kernel mmzone.h */
-#define MAX_NR_ZONES 6
-
-union zoneinfo_zone_fields {
+union zoneinfo {
     struct {
         int64_t nr_free_pages;
-        int64_t min;
-        int64_t low;
-        int64_t high;
-        int64_t present;
-        int64_t nr_free_cma;
-    } field;
-    int64_t arr[ZI_ZONE_FIELD_COUNT];
-};
-
-struct zoneinfo_zone {
-    union zoneinfo_zone_fields fields;
-    int64_t protection[MAX_NR_ZONES];
-    int64_t max_protection;
-};
-
-/* zoneinfo per-node fields */
-enum zoneinfo_node_field {
-    ZI_NODE_NR_INACTIVE_FILE = 0,
-    ZI_NODE_NR_ACTIVE_FILE,
-    ZI_NODE_WORKINGSET_REFAULT,
-    ZI_NODE_FIELD_COUNT
-};
-
-static const char* const zoneinfo_node_field_names[ZI_NODE_FIELD_COUNT] = {
-    "nr_inactive_file",
-    "nr_active_file",
-    "workingset_refault",
-};
-
-union zoneinfo_node_fields {
-    struct {
-        int64_t nr_inactive_file;
-        int64_t nr_active_file;
+        int64_t nr_file_pages;
+        int64_t nr_shmem;
+        int64_t nr_unevictable;
         int64_t workingset_refault;
+        int64_t high;
+        /* fields below are calculated rather than read from the file */
+        int64_t totalreserve_pages;
     } field;
-    int64_t arr[ZI_NODE_FIELD_COUNT];
-};
-
-struct zoneinfo_node {
-    int id;
-    int zone_count;
-    struct zoneinfo_zone zones[MAX_NR_ZONES];
-    union zoneinfo_node_fields fields;
-};
-
-/* for now two memory nodes is more than enough */
-#define MAX_NR_NODES 2
-
-struct zoneinfo {
-    int node_count;
-    struct zoneinfo_node nodes[MAX_NR_NODES];
-    int64_t totalreserve_pages;
-    int64_t total_inactive_file;
-    int64_t total_active_file;
-    int64_t total_workingset_refault;
+    int64_t arr[ZI_FIELD_COUNT];
 };
 
 /* Fields to parse in /proc/meminfo */
@@ -440,41 +310,6 @@
     int64_t arr[MI_FIELD_COUNT];
 };
 
-/* Fields to parse in /proc/vmstat */
-enum vmstat_field {
-    VS_FREE_PAGES,
-    VS_INACTIVE_FILE,
-    VS_ACTIVE_FILE,
-    VS_WORKINGSET_REFAULT,
-    VS_PGSCAN_KSWAPD,
-    VS_PGSCAN_DIRECT,
-    VS_PGSCAN_DIRECT_THROTTLE,
-    VS_FIELD_COUNT
-};
-
-static const char* const vmstat_field_names[MI_FIELD_COUNT] = {
-    "nr_free_pages",
-    "nr_inactive_file",
-    "nr_active_file",
-    "workingset_refault",
-    "pgscan_kswapd",
-    "pgscan_direct",
-    "pgscan_direct_throttle",
-};
-
-union vmstat {
-    struct {
-        int64_t nr_free_pages;
-        int64_t nr_inactive_file;
-        int64_t nr_active_file;
-        int64_t workingset_refault;
-        int64_t pgscan_kswapd;
-        int64_t pgscan_direct;
-        int64_t pgscan_direct_throttle;
-    } field;
-    int64_t arr[VS_FIELD_COUNT];
-};
-
 enum field_match_result {
     NO_MATCH,
     PARSE_FAIL,
@@ -489,10 +324,8 @@
 struct proc {
     struct adjslot_list asl;
     int pid;
-    int pidfd;
     uid_t uid;
     int oomadj;
-    pid_t reg_pid; /* PID of the process that registered this record */
     struct proc *pidhash_next;
 };
 
@@ -501,6 +334,11 @@
     int fd;
 };
 
+#ifdef LMKD_LOG_STATS
+static bool enable_stats_log;
+static android_log_context log_ctx;
+#endif
+
 #define PIDHASH_SZ 1024
 static struct proc *pidhash[PIDHASH_SZ];
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
@@ -524,10 +362,6 @@
 /* PAGE_SIZE / 1024 */
 static long page_k;
 
-static int clamp(int low, int high, int value) {
-    return max(min(value, high), low);
-}
-
 static bool parse_int64(const char* str, int64_t* ret) {
     char* endptr;
     long long val = strtoll(str, &endptr, 10);
@@ -538,25 +372,20 @@
     return true;
 }
 
-static int find_field(const char* name, const char* const field_names[], int field_count) {
-    for (int i = 0; i < field_count; i++) {
-        if (!strcmp(name, field_names[i])) {
-            return i;
-        }
-    }
-    return -1;
-}
-
 static enum field_match_result match_field(const char* cp, const char* ap,
                                    const char* const field_names[],
                                    int field_count, int64_t* field,
                                    int *field_idx) {
-    int i = find_field(cp, field_names, field_count);
-    if (i < 0) {
-        return NO_MATCH;
+    int64_t val;
+    int i;
+
+    for (i = 0; i < field_count; i++) {
+        if (!strcmp(cp, field_names[i])) {
+            *field_idx = i;
+            return parse_int64(ap, field) ? PARSE_SUCCESS : PARSE_FAIL;
+        }
     }
-    *field_idx = i;
-    return parse_int64(ap, field) ? PARSE_SUCCESS : PARSE_FAIL;
+    return NO_MATCH;
 }
 
 /*
@@ -592,50 +421,28 @@
  * memory pressure to minimize file opening which by itself requires kernel
  * memory allocation and might result in a stall on memory stressed system.
  */
-static char *reread_file(struct reread_data *data) {
-    /* start with page-size buffer and increase if needed */
-    static ssize_t buf_size = PAGE_SIZE;
-    static char *new_buf, *buf = NULL;
+static int reread_file(struct reread_data *data, char *buf, size_t buf_size) {
     ssize_t size;
 
     if (data->fd == -1) {
-        /* First-time buffer initialization */
-        if (!buf && (buf = malloc(buf_size)) == NULL) {
-            return NULL;
-        }
-
-        data->fd = TEMP_FAILURE_RETRY(open(data->filename, O_RDONLY | O_CLOEXEC));
-        if (data->fd < 0) {
+        data->fd = open(data->filename, O_RDONLY | O_CLOEXEC);
+        if (data->fd == -1) {
             ALOGE("%s open: %s", data->filename, strerror(errno));
-            return NULL;
+            return -1;
         }
     }
 
-    while (true) {
-        size = read_all(data->fd, buf, buf_size - 1);
-        if (size < 0) {
-            ALOGE("%s read: %s", data->filename, strerror(errno));
-            close(data->fd);
-            data->fd = -1;
-            return NULL;
-        }
-        if (size < buf_size - 1) {
-            break;
-        }
-        /*
-         * Since we are reading /proc files we can't use fstat to find out
-         * the real size of the file. Double the buffer size and keep retrying.
-         */
-        if ((new_buf = realloc(buf, buf_size * 2)) == NULL) {
-            errno = ENOMEM;
-            return NULL;
-        }
-        buf = new_buf;
-        buf_size *= 2;
+    size = read_all(data->fd, buf, buf_size - 1);
+    if (size < 0) {
+        ALOGE("%s read: %s", data->filename, strerror(errno));
+        close(data->fd);
+        data->fd = -1;
+        return -1;
     }
+    ALOG_ASSERT((size_t)size < buf_size - 1, "%s too large", data->filename);
     buf[size] = 0;
 
-    return buf;
+    return 0;
 }
 
 static struct proc *pid_lookup(int pid) {
@@ -707,13 +514,6 @@
         prevp->pidhash_next = procp->pidhash_next;
 
     proc_unslot(procp);
-    /*
-     * Close pidfd here if we are not waiting for corresponding process to die,
-     * in which case stop_wait_for_proc_kill() will close the pidfd later
-     */
-    if (procp->pidfd >= 0 && procp->pidfd != last_kill_pid_or_fd) {
-        close(procp->pidfd);
-    }
     free(procp);
     return 0;
 }
@@ -795,90 +595,9 @@
     return (int)tgid;
 }
 
-static int proc_get_size(int pid) {
-    char path[PATH_MAX];
-    char line[LINE_MAX];
-    int fd;
-    int rss = 0;
-    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)
-        return -1;
-
-    ret = read_all(fd, line, sizeof(line) - 1);
-    if (ret < 0) {
-        close(fd);
-        return -1;
-    }
-    line[ret] = '\0';
-
-    sscanf(line, "%d %d ", &total, &rss);
-    close(fd);
-    return rss;
-}
-
-static char *proc_get_name(int pid, char *buf, size_t buf_size) {
-    char path[PATH_MAX];
-    int fd;
-    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) {
-        return NULL;
-    }
-    ret = read_all(fd, buf, buf_size - 1);
-    close(fd);
-    if (ret < 0) {
-        return NULL;
-    }
-    buf[ret] = '\0';
-
-    cp = strchr(buf, ' ');
-    if (cp) {
-        *cp = '\0';
-    }
-
-    return buf;
-}
-
-static bool claim_record(struct proc *procp, pid_t pid) {
-    if (procp->reg_pid == pid) {
-        /* Record already belongs to the registrant */
-        return true;
-    }
-    if (procp->reg_pid == 0) {
-        /* Old registrant is gone, claim the record */
-        procp->reg_pid = pid;
-        return true;
-    }
-    /* The record is owned by another registrant */
-    return false;
-}
-
-static void remove_claims(pid_t pid) {
-    int i;
-
-    for (i = 0; i < PIDHASH_SZ; i++) {
-        struct proc *procp = pidhash[i];
-        while (procp) {
-            if (procp->reg_pid == pid) {
-                procp->reg_pid = 0;
-            }
-            procp = procp->pidhash_next;
-        }
-    }
-}
-
-static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred *cred) {
+static void cmd_procprio(LMKD_CTRL_PACKET packet) {
     struct proc *procp;
-    char path[LINE_MAX];
+    char path[80];
     char val[20];
     int soft_limit_mult;
     struct lmk_procprio params;
@@ -886,7 +605,7 @@
     struct passwd *pwdrec;
     int tgid;
 
-    lmkd_pack_get_procprio(packet, field_count, &params);
+    lmkd_pack_get_procprio(packet, &params);
 
     if (params.oomadj < OOM_SCORE_ADJ_MIN ||
         params.oomadj > OOM_SCORE_ADJ_MAX) {
@@ -894,11 +613,6 @@
         return;
     }
 
-    if (params.ptype < PROC_TYPE_FIRST || params.ptype >= PROC_TYPE_COUNT) {
-        ALOGE("Invalid PROCPRIO process type argument %d", params.ptype);
-        return;
-    }
-
     /* Check if registered process is a thread group leader */
     tgid = proc_get_tgid(params.pid);
     if (tgid >= 0 && tgid != params.pid) {
@@ -920,13 +634,10 @@
     }
 
     if (use_inkernel_interface) {
-        stats_store_taskname(params.pid, proc_get_name(params.pid, path, sizeof(path)),
-                             kpoll_info.poll_fd);
         return;
     }
 
-    /* lmkd should not change soft limits for services */
-    if (params.ptype == PROC_TYPE_APP && per_app_memcg) {
+    if (per_app_memcg) {
         if (params.oomadj >= 900) {
             soft_limit_mult = 0;
         } else if (params.oomadj >= 800) {
@@ -972,66 +683,31 @@
 
     procp = pid_lookup(params.pid);
     if (!procp) {
-        int pidfd = -1;
-
-        if (pidfd_supported) {
-            pidfd = TEMP_FAILURE_RETRY(sys_pidfd_open(params.pid, 0));
-            if (pidfd < 0) {
-                ALOGE("pidfd_open for pid %d failed; errno=%d", params.pid, errno);
+            procp = malloc(sizeof(struct proc));
+            if (!procp) {
+                // Oh, the irony.  May need to rebuild our state.
                 return;
             }
-        }
 
-        procp = calloc(1, sizeof(struct proc));
-        if (!procp) {
-            // Oh, the irony.  May need to rebuild our state.
-            return;
-        }
-
-        procp->pid = params.pid;
-        procp->pidfd = pidfd;
-        procp->uid = params.uid;
-        procp->reg_pid = cred->pid;
-        procp->oomadj = params.oomadj;
-        proc_insert(procp);
+            procp->pid = params.pid;
+            procp->uid = params.uid;
+            procp->oomadj = params.oomadj;
+            proc_insert(procp);
     } else {
-        if (!claim_record(procp, cred->pid)) {
-            char buf[LINE_MAX];
-            /* Only registrant of the record can remove it */
-            ALOGE("%s (%d, %d) attempts to modify a process registered by another client",
-                proc_get_name(cred->pid, buf, sizeof(buf)), cred->uid, cred->pid);
-            return;
-        }
         proc_unslot(procp);
         procp->oomadj = params.oomadj;
         proc_slot(procp);
     }
 }
 
-static void cmd_procremove(LMKD_CTRL_PACKET packet, struct ucred *cred) {
+static void cmd_procremove(LMKD_CTRL_PACKET packet) {
     struct lmk_procremove params;
-    struct proc *procp;
-
-    lmkd_pack_get_procremove(packet, &params);
 
     if (use_inkernel_interface) {
-        stats_remove_taskname(params.pid, kpoll_info.poll_fd);
         return;
     }
 
-    procp = pid_lookup(params.pid);
-    if (!procp) {
-        return;
-    }
-
-    if (!claim_record(procp, cred->pid)) {
-        char buf[LINE_MAX];
-        /* Only registrant of the record can remove it */
-        ALOGE("%s (%d, %d) attempts to unregister a process registered by another client",
-            proc_get_name(cred->pid, buf, sizeof(buf)), cred->uid, cred->pid);
-        return;
-    }
-
+    lmkd_pack_get_procremove(packet, &params);
     /*
      * WARNING: After pid_remove() procp is freed and can't be used!
      * Therefore placed at the end of the function.
@@ -1039,27 +715,29 @@
     pid_remove(params.pid);
 }
 
-static void cmd_procpurge(struct ucred *cred) {
+static void cmd_procpurge() {
     int i;
     struct proc *procp;
     struct proc *next;
 
     if (use_inkernel_interface) {
-        stats_purge_tasknames();
         return;
     }
 
+    for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
+        procadjslot_list[i].next = &procadjslot_list[i];
+        procadjslot_list[i].prev = &procadjslot_list[i];
+    }
+
     for (i = 0; i < PIDHASH_SZ; i++) {
         procp = pidhash[i];
         while (procp) {
             next = procp->pidhash_next;
-            /* Purge only records created by the requestor */
-            if (claim_record(procp, cred->pid)) {
-                pid_remove(procp->pid);
-            }
+            free(procp);
             procp = next;
         }
     }
+    memset(&pidhash[0], 0, sizeof(pidhash));
 }
 
 static void inc_killcnt(int oomadj) {
@@ -1210,51 +888,20 @@
 
     close(data_sock[dsock_idx].sock);
     data_sock[dsock_idx].sock = -1;
-
-    /* Mark all records of the old registrant as unclaimed */
-    remove_claims(data_sock[dsock_idx].pid);
 }
 
-static ssize_t ctrl_data_read(int dsock_idx, char *buf, size_t bufsz, struct ucred *sender_cred) {
-    struct iovec iov = { buf, bufsz };
-    char control[CMSG_SPACE(sizeof(struct ucred))];
-    struct msghdr hdr = {
-        NULL, 0, &iov, 1, control, sizeof(control), 0,
-    };
-    ssize_t ret;
+static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
+    int ret = 0;
 
-    ret = TEMP_FAILURE_RETRY(recvmsg(data_sock[dsock_idx].sock, &hdr, 0));
+    ret = TEMP_FAILURE_RETRY(read(data_sock[dsock_idx].sock, buf, bufsz));
+
     if (ret == -1) {
-        ALOGE("control data socket read failed; %s", strerror(errno));
-        return -1;
-    }
-    if (ret == 0) {
+        ALOGE("control data socket read failed; errno=%d", errno);
+    } else if (ret == 0) {
         ALOGE("Got EOF on control data socket");
-        return -1;
+        ret = -1;
     }
 
-    struct ucred* cred = NULL;
-    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
-    while (cmsg != NULL) {
-        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {
-            cred = (struct ucred*)CMSG_DATA(cmsg);
-            break;
-        }
-        cmsg = CMSG_NXTHDR(&hdr, cmsg);
-    }
-
-    if (cred == NULL) {
-        ALOGE("Failed to retrieve sender credentials");
-        /* Close the connection */
-        ctrl_data_close(dsock_idx);
-        return -1;
-    }
-
-    memcpy(sender_cred, cred, sizeof(struct ucred));
-
-    /* Store PID of the peer */
-    data_sock[dsock_idx].pid = cred->pid;
-
     return ret;
 }
 
@@ -1275,14 +922,13 @@
 
 static void ctrl_command_handler(int dsock_idx) {
     LMKD_CTRL_PACKET packet;
-    struct ucred cred;
     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, &cred);
+    len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
     if (len <= 0)
         return;
 
@@ -1304,20 +950,19 @@
         cmd_target(targets, packet);
         break;
     case LMK_PROCPRIO:
-        /* process type field is optional for backward compatibility */
-        if (nargs < 3 || nargs > 4)
+        if (nargs != 3)
             goto wronglen;
-        cmd_procprio(packet, nargs, &cred);
+        cmd_procprio(packet);
         break;
     case LMK_PROCREMOVE:
         if (nargs != 1)
             goto wronglen;
-        cmd_procremove(packet, &cred);
+        cmd_procremove(packet);
         break;
     case LMK_PROCPURGE:
         if (nargs != 0)
             goto wronglen;
-        cmd_procpurge(&cred);
+        cmd_procpurge();
         break;
     case LMK_GETKILLCNT:
         if (nargs != 2)
@@ -1338,8 +983,7 @@
     ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
 }
 
-static void ctrl_data_handler(int data, uint32_t events,
-                              struct polling_params *poll_params __unused) {
+static void ctrl_data_handler(int data, uint32_t events) {
     if (events & EPOLLIN) {
         ctrl_command_handler(data);
     }
@@ -1354,8 +998,7 @@
     return -1;
 }
 
-static void ctrl_connect_handler(int data __unused, uint32_t events __unused,
-                                 struct polling_params *poll_params __unused) {
+static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
     struct epoll_event epev;
     int free_dscock_idx = get_free_dsock();
 
@@ -1393,202 +1036,174 @@
     maxevents++;
 }
 
-/*
- * /proc/zoneinfo parsing routines
- * Expected file format is:
- *
- *   Node <node_id>, zone   <zone_name>
- *   (
- *    per-node stats
- *       (<per-node field name> <value>)+
- *   )?
- *   (pages free     <value>
- *       (<per-zone field name> <value>)+
- *    pagesets
- *       (<unused fields>)*
- *   )+
- *   ...
- */
-static void zoneinfo_parse_protection(char *buf, struct zoneinfo_zone *zone) {
-    int zone_idx;
+#ifdef LMKD_LOG_STATS
+static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
+    char key[LINE_MAX + 1];
+    int64_t value;
+
+    sscanf(line, "%" STRINGIFY(LINE_MAX) "s  %" SCNd64 "", key, &value);
+
+    if (strcmp(key, "total_") < 0) {
+        return;
+    }
+
+    if (!strcmp(key, "total_pgfault"))
+        mem_st->pgfault = value;
+    else if (!strcmp(key, "total_pgmajfault"))
+        mem_st->pgmajfault = value;
+    else if (!strcmp(key, "total_rss"))
+        mem_st->rss_in_bytes = value;
+    else if (!strcmp(key, "total_cache"))
+        mem_st->cache_in_bytes = value;
+    else if (!strcmp(key, "total_swap"))
+        mem_st->swap_in_bytes = value;
+}
+
+static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
+    FILE *fp;
+    char buf[PATH_MAX];
+
+    snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
+
+    fp = fopen(buf, "r");
+
+    if (fp == NULL) {
+        ALOGE("%s open failed: %s", buf, strerror(errno));
+        return -1;
+    }
+
+    while (fgets(buf, PAGE_SIZE, fp) != NULL) {
+        memory_stat_parse_line(buf, mem_st);
+    }
+    fclose(fp);
+
+    return 0;
+}
+
+static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
+    char path[PATH_MAX];
+    char buffer[PROC_STAT_BUFFER_SIZE];
+    int fd, ret;
+
+    snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
+    if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
+        ALOGE("%s open failed: %s", path, strerror(errno));
+        return -1;
+    }
+
+    ret = read(fd, buffer, sizeof(buffer));
+    if (ret < 0) {
+        ALOGE("%s read failed: %s", path, strerror(errno));
+        close(fd);
+        return -1;
+    }
+    close(fd);
+
+    // field 10 is pgfault
+    // field 12 is pgmajfault
+    // field 22 is starttime
+    // field 24 is rss_in_pages
+    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 "
+               "%" 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
+
+/* /prop/zoneinfo parsing routines */
+static int64_t zoneinfo_parse_protection(char *cp) {
     int64_t max = 0;
+    long long zoneval;
     char *save_ptr;
 
-    for (buf = strtok_r(buf, "(), ", &save_ptr), zone_idx = 0;
-         buf && zone_idx < MAX_NR_ZONES;
-         buf = strtok_r(NULL, "), ", &save_ptr), zone_idx++) {
-        long long zoneval = strtoll(buf, &buf, 0);
+    for (cp = strtok_r(cp, "(), ", &save_ptr); cp;
+         cp = strtok_r(NULL, "), ", &save_ptr)) {
+        zoneval = strtoll(cp, &cp, 0);
         if (zoneval > max) {
             max = (zoneval > INT64_MAX) ? INT64_MAX : zoneval;
         }
-        zone->protection[zone_idx] = zoneval;
     }
-    zone->max_protection = max;
+
+    return max;
 }
 
-static int zoneinfo_parse_zone(char **buf, struct zoneinfo_zone *zone) {
-    for (char *line = strtok_r(NULL, "\n", buf); line;
-         line = strtok_r(NULL, "\n", buf)) {
-        char *cp;
-        char *ap;
-        char *save_ptr;
-        int64_t val;
-        int field_idx;
-        enum field_match_result match_res;
+static bool zoneinfo_parse_line(char *line, union zoneinfo *zi) {
+    char *cp = line;
+    char *ap;
+    char *save_ptr;
+    int64_t val;
+    int field_idx;
 
-        cp = strtok_r(line, " ", &save_ptr);
-        if (!cp) {
-            return false;
-        }
+    cp = strtok_r(line, " ", &save_ptr);
+    if (!cp) {
+        return true;
+    }
 
-        field_idx = find_field(cp, zoneinfo_zone_spec_field_names, ZI_ZONE_SPEC_FIELD_COUNT);
-        if (field_idx >= 0) {
-            /* special field */
-            if (field_idx == ZI_ZONE_SPEC_PAGESETS) {
-                /* no mode fields we are interested in */
-                return true;
-            }
-
-            /* protection field */
-            ap = strtok_r(NULL, ")", &save_ptr);
-            if (ap) {
-                zoneinfo_parse_protection(ap, zone);
-            }
-            continue;
-        }
-
+    if (!strcmp(cp, "protection:")) {
+        ap = strtok_r(NULL, ")", &save_ptr);
+    } else {
         ap = strtok_r(NULL, " ", &save_ptr);
-        if (!ap) {
-            continue;
-        }
-
-        match_res = match_field(cp, ap, zoneinfo_zone_field_names, ZI_ZONE_FIELD_COUNT,
-            &val, &field_idx);
-        if (match_res == PARSE_FAIL) {
-            return false;
-        }
-        if (match_res == PARSE_SUCCESS) {
-            zone->fields.arr[field_idx] = val;
-        }
-        if (field_idx == ZI_ZONE_PRESENT && val == 0) {
-            /* zone is not populated, stop parsing it */
-            return true;
-        }
     }
-    return false;
+
+    if (!ap) {
+        return true;
+    }
+
+    switch (match_field(cp, ap, zoneinfo_field_names,
+                        ZI_FIELD_COUNT, &val, &field_idx)) {
+    case (PARSE_SUCCESS):
+        zi->arr[field_idx] += val;
+        break;
+    case (NO_MATCH):
+        if (!strcmp(cp, "protection:")) {
+            zi->field.totalreserve_pages +=
+                zoneinfo_parse_protection(ap);
+        }
+        break;
+    case (PARSE_FAIL):
+    default:
+        return false;
+    }
+    return true;
 }
 
-static int zoneinfo_parse_node(char **buf, struct zoneinfo_node *node) {
-    int fields_to_match = ZI_NODE_FIELD_COUNT;
-
-    for (char *line = strtok_r(NULL, "\n", buf); line;
-         line = strtok_r(NULL, "\n", buf)) {
-        char *cp;
-        char *ap;
-        char *save_ptr;
-        int64_t val;
-        int field_idx;
-        enum field_match_result match_res;
-
-        cp = strtok_r(line, " ", &save_ptr);
-        if (!cp) {
-            return false;
-        }
-
-        ap = strtok_r(NULL, " ", &save_ptr);
-        if (!ap) {
-            return false;
-        }
-
-        match_res = match_field(cp, ap, zoneinfo_node_field_names, ZI_NODE_FIELD_COUNT,
-            &val, &field_idx);
-        if (match_res == PARSE_FAIL) {
-            return false;
-        }
-        if (match_res == PARSE_SUCCESS) {
-            node->fields.arr[field_idx] = val;
-            fields_to_match--;
-            if (!fields_to_match) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-static int zoneinfo_parse(struct zoneinfo *zi) {
+static int zoneinfo_parse(union zoneinfo *zi) {
     static struct reread_data file_data = {
         .filename = ZONEINFO_PATH,
         .fd = -1,
     };
-    char *buf;
+    char buf[PAGE_SIZE];
     char *save_ptr;
     char *line;
-    char zone_name[LINE_MAX + 1];
-    struct zoneinfo_node *node = NULL;
-    int node_idx = 0;
-    int zone_idx = 0;
 
-    memset(zi, 0, sizeof(struct zoneinfo));
+    memset(zi, 0, sizeof(union zoneinfo));
 
-    if ((buf = reread_file(&file_data)) == NULL) {
+    if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
         return -1;
     }
 
     for (line = strtok_r(buf, "\n", &save_ptr); line;
          line = strtok_r(NULL, "\n", &save_ptr)) {
-        int node_id;
-        if (sscanf(line, "Node %d, zone %" STRINGIFY(LINE_MAX) "s", &node_id, zone_name) == 2) {
-            if (!node || node->id != node_id) {
-                /* new node is found */
-                if (node) {
-                    node->zone_count = zone_idx + 1;
-                    node_idx++;
-                    if (node_idx == MAX_NR_NODES) {
-                        /* max node count exceeded */
-                        ALOGE("%s parse error", file_data.filename);
-                        return -1;
-                    }
-                }
-                node = &zi->nodes[node_idx];
-                node->id = node_id;
-                zone_idx = 0;
-                if (!zoneinfo_parse_node(&save_ptr, node)) {
-                    ALOGE("%s parse error", file_data.filename);
-                    return -1;
-                }
-            } else {
-                /* new zone is found */
-                zone_idx++;
-            }
-            if (!zoneinfo_parse_zone(&save_ptr, &node->zones[zone_idx])) {
-                ALOGE("%s parse error", file_data.filename);
-                return -1;
-            }
+        if (!zoneinfo_parse_line(line, zi)) {
+            ALOGE("%s parse error", file_data.filename);
+            return -1;
         }
     }
-    if (!node) {
-        ALOGE("%s parse error", file_data.filename);
-        return -1;
-    }
-    node->zone_count = zone_idx + 1;
-    zi->node_count = node_idx + 1;
+    zi->field.totalreserve_pages += zi->field.high;
 
-    /* calculate totals fields */
-    for (node_idx = 0; node_idx < zi->node_count; node_idx++) {
-        node = &zi->nodes[node_idx];
-        for (zone_idx = 0; zone_idx < node->zone_count; zone_idx++) {
-            struct zoneinfo_zone *zone = &zi->nodes[node_idx].zones[zone_idx];
-            zi->totalreserve_pages += zone->max_protection + zone->fields.field.high;
-        }
-        zi->total_inactive_file += node->fields.field.nr_inactive_file;
-        zi->total_active_file += node->fields.field.nr_active_file;
-        zi->total_workingset_refault += node->fields.field.workingset_refault;
-    }
     return 0;
 }
 
-/* /proc/meminfo parsing routines */
+/* /prop/meminfo parsing routines */
 static bool meminfo_parse_line(char *line, union meminfo *mi) {
     char *cp = line;
     char *ap;
@@ -1620,13 +1235,13 @@
         .filename = MEMINFO_PATH,
         .fd = -1,
     };
-    char *buf;
+    char buf[PAGE_SIZE];
     char *save_ptr;
     char *line;
 
     memset(mi, 0, sizeof(union meminfo));
 
-    if ((buf = reread_file(&file_data)) == NULL) {
+    if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
         return -1;
     }
 
@@ -1643,70 +1258,7 @@
     return 0;
 }
 
-/* /proc/vmstat parsing routines */
-static bool vmstat_parse_line(char *line, union vmstat *vs) {
-    char *cp;
-    char *ap;
-    char *save_ptr;
-    int64_t val;
-    int field_idx;
-    enum field_match_result match_res;
-
-    cp = strtok_r(line, " ", &save_ptr);
-    if (!cp) {
-        return false;
-    }
-
-    ap = strtok_r(NULL, " ", &save_ptr);
-    if (!ap) {
-        return false;
-    }
-
-    match_res = match_field(cp, ap, vmstat_field_names, VS_FIELD_COUNT,
-        &val, &field_idx);
-    if (match_res == PARSE_SUCCESS) {
-        vs->arr[field_idx] = val;
-    }
-    return (match_res != PARSE_FAIL);
-}
-
-static int vmstat_parse(union vmstat *vs) {
-    static struct reread_data file_data = {
-        .filename = VMSTAT_PATH,
-        .fd = -1,
-    };
-    char *buf;
-    char *save_ptr;
-    char *line;
-
-    memset(vs, 0, sizeof(union vmstat));
-
-    if ((buf = reread_file(&file_data)) == NULL) {
-        return -1;
-    }
-
-    for (line = strtok_r(buf, "\n", &save_ptr); line;
-         line = strtok_r(NULL, "\n", &save_ptr)) {
-        if (!vmstat_parse_line(line, vs)) {
-            ALOGE("%s parse error", file_data.filename);
-            return -1;
-        }
-    }
-
-    return 0;
-}
-
-static void killinfo_log(struct proc* procp, int min_oom_score, int tasksize,
-                         int kill_reason, union meminfo *mi) {
-    /* log process information */
-    android_log_write_int32(ctx, procp->pid);
-    android_log_write_int32(ctx, procp->uid);
-    android_log_write_int32(ctx, procp->oomadj);
-    android_log_write_int32(ctx, min_oom_score);
-    android_log_write_int32(ctx, (int32_t)min(tasksize * page_k, INT32_MAX));
-    android_log_write_int32(ctx, kill_reason);
-
-    /* log meminfo fields */
+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));
     }
@@ -1715,6 +1267,56 @@
     android_log_reset(ctx);
 }
 
+static int proc_get_size(int pid) {
+    char path[PATH_MAX];
+    char line[LINE_MAX];
+    int fd;
+    int rss = 0;
+    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)
+        return -1;
+
+    ret = read_all(fd, line, sizeof(line) - 1);
+    if (ret < 0) {
+        close(fd);
+        return -1;
+    }
+
+    sscanf(line, "%d %d ", &total, &rss);
+    close(fd);
+    return rss;
+}
+
+static char *proc_get_name(int pid) {
+    char path[PATH_MAX];
+    static char line[LINE_MAX];
+    int fd;
+    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)
+        return NULL;
+    ret = read_all(fd, line, sizeof(line) - 1);
+    close(fd);
+    if (ret < 0) {
+        return NULL;
+    }
+
+    cp = strchr(line, ' ');
+    if (cp)
+        *cp = '\0';
+
+    return line;
+}
+
 static struct proc *proc_adj_lru(int oomadj) {
     return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
 }
@@ -1777,117 +1379,25 @@
     closedir(d);
 }
 
-static bool is_kill_pending(void) {
-    char buf[24];
-
-    if (last_kill_pid_or_fd < 0) {
-        return false;
-    }
-
-    if (pidfd_supported) {
-        return true;
-    }
-
-    /* when pidfd is not supported base the decision on /proc/<pid> existence */
-    snprintf(buf, sizeof(buf), "/proc/%d/", last_kill_pid_or_fd);
-    if (access(buf, F_OK) == 0) {
-        return true;
-    }
-
-    return false;
-}
-
-static bool is_waiting_for_kill(void) {
-    return pidfd_supported && last_kill_pid_or_fd >= 0;
-}
-
-static void stop_wait_for_proc_kill(bool finished) {
-    struct epoll_event epev;
-
-    if (last_kill_pid_or_fd < 0) {
-        return;
-    }
-
-    if (debug_process_killing) {
-        struct timespec curr_tm;
-
-        if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
-            /*
-             * curr_tm is used here merely to report kill duration, so this failure is not fatal.
-             * Log an error and continue.
-             */
-            ALOGE("Failed to get current time");
-        }
-
-        if (finished) {
-            ALOGI("Process got killed in %ldms",
-                get_time_diff_ms(&last_kill_tm, &curr_tm));
-        } else {
-            ALOGI("Stop waiting for process kill after %ldms",
-                get_time_diff_ms(&last_kill_tm, &curr_tm));
-        }
-    }
-
-    if (pidfd_supported) {
-        /* unregister fd */
-        if (epoll_ctl(epollfd, EPOLL_CTL_DEL, last_kill_pid_or_fd, &epev) != 0) {
-            ALOGE("epoll_ctl for last killed process failed; errno=%d", errno);
-            return;
-        }
-        maxevents--;
-        close(last_kill_pid_or_fd);
-    }
-
-    last_kill_pid_or_fd = -1;
-}
-
-static void kill_done_handler(int data __unused, uint32_t events __unused,
-                              struct polling_params *poll_params) {
-    stop_wait_for_proc_kill(true);
-    poll_params->update = POLLING_RESUME;
-}
-
-static void start_wait_for_proc_kill(int pid_or_fd) {
-    static struct event_handler_info kill_done_hinfo = { 0, kill_done_handler };
-    struct epoll_event epev;
-
-    if (last_kill_pid_or_fd >= 0) {
-        /* Should not happen but if it does we should stop previous wait */
-        ALOGE("Attempt to wait for a kill while another wait is in progress");
-        stop_wait_for_proc_kill(false);
-    }
-
-    last_kill_pid_or_fd = pid_or_fd;
-
-    if (!pidfd_supported) {
-        /* If pidfd is not supported just store PID and exit */
-        return;
-    }
-
-    epev.events = EPOLLIN;
-    epev.data.ptr = (void *)&kill_done_hinfo;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, last_kill_pid_or_fd, &epev) != 0) {
-        ALOGE("epoll_ctl for last kill failed; errno=%d", errno);
-        close(last_kill_pid_or_fd);
-        last_kill_pid_or_fd = -1;
-        return;
-    }
-    maxevents++;
-}
+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, int min_oom_score, int kill_reason,
-                            const char *kill_desc, union meminfo *mi, struct timespec *tm) {
+static int kill_one_process(struct proc* procp, int min_oom_score) {
     int pid = procp->pid;
-    int pidfd = procp->pidfd;
     uid_t uid = procp->uid;
     int tgid;
     char *taskname;
     int tasksize;
     int r;
     int result = -1;
-    struct memory_stat *mem_st;
-    char buf[LINE_MAX];
+
+#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
 
     tgid = proc_get_tgid(pid);
     if (tgid >= 0 && tgid != pid) {
@@ -1895,7 +1405,7 @@
         goto out;
     }
 
-    taskname = proc_get_name(pid, buf, sizeof(buf));
+    taskname = proc_get_name(pid);
     if (!taskname) {
         goto out;
     }
@@ -1905,49 +1415,50 @@
         goto out;
     }
 
-    mem_st = stats_read_memory_stat(per_app_memcg, pid, uid);
+#ifdef LMKD_LOG_STATS
+    if (enable_stats_log) {
+        if (per_app_memcg) {
+            memory_stat_parse_result = memory_stat_from_cgroup(&mem_st, pid, uid);
+        } else {
+            memory_stat_parse_result = memory_stat_from_procfs(&mem_st, pid);
+        }
+    }
+#endif
 
     TRACE_KILL_START(pid);
 
     /* CAP_KILL required */
-    if (pidfd < 0) {
-        start_wait_for_proc_kill(pid);
-        r = kill(pid, SIGKILL);
-    } else {
-        start_wait_for_proc_kill(pidfd);
-        r = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
-    }
-
-    TRACE_KILL_END();
-
-    if (r) {
-        stop_wait_for_proc_kill(false);
-        ALOGE("kill(%d): errno=%d", pid, errno);
-        /* Delete process record even when we fail to kill so that we don't get stuck on it */
-        goto out;
-    }
+    r = kill(pid, SIGKILL);
 
     set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
 
-    last_kill_tm = *tm;
-
     inc_killcnt(procp->oomadj);
+    ALOGE("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid, uid, procp->oomadj,
+          tasksize * page_k);
 
-    killinfo_log(procp, min_oom_score, tasksize, kill_reason, mi);
+    TRACE_KILL_END();
 
-    if (kill_desc) {
-        ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB; reason: %s", taskname, pid,
-              uid, procp->oomadj, tasksize * page_k, kill_desc);
+    last_killed_pid = pid;
+
+    if (r) {
+        ALOGE("kill(%d): errno=%d", pid, errno);
+        goto out;
     } else {
-        ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid,
-              uid, procp->oomadj, tasksize * page_k);
+#ifdef LMKD_LOG_STATS
+        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.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,
+                                          min_oom_score);
+        }
+#endif
+        result = tasksize;
     }
 
-    stats_write_lmk_kill_occurred(LMK_KILL_OCCURRED, uid, taskname,
-            procp->oomadj, min_oom_score, tasksize, mem_st);
-
-    result = tasksize;
-
 out:
     /*
      * WARNING: After pid_remove() procp is freed and can't be used!
@@ -1961,11 +1472,13 @@
  * Find one process to kill at or above the given oom_adj level.
  * Returns size of the killed process.
  */
-static int find_and_kill_process(int min_score_adj, int kill_reason, const char *kill_desc,
-                                 union meminfo *mi, struct timespec *tm) {
+static int find_and_kill_process(int min_score_adj) {
     int i;
     int killed_size = 0;
+
+#ifdef LMKD_LOG_STATS
     bool lmk_state_change_start = false;
+#endif
 
     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
@@ -1977,13 +1490,15 @@
             if (!procp)
                 break;
 
-            killed_size = kill_one_process(procp, min_score_adj, kill_reason, kill_desc, mi, tm);
+            killed_size = kill_one_process(procp, min_score_adj);
             if (killed_size >= 0) {
-                if (!lmk_state_change_start) {
+#ifdef LMKD_LOG_STATS
+                if (enable_stats_log && !lmk_state_change_start) {
                     lmk_state_change_start = true;
-                    stats_write_lmk_state_changed(LMK_STATE_CHANGED,
+                    stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
                                                   LMK_STATE_CHANGE_START);
                 }
+#endif
                 break;
             }
         }
@@ -1992,9 +1507,11 @@
         }
     }
 
-    if (lmk_state_change_start) {
-        stats_write_lmk_state_changed(LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
+#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 killed_size;
 }
@@ -2002,9 +1519,9 @@
 static int64_t get_memory_usage(struct reread_data *file_data) {
     int ret;
     int64_t mem_usage;
-    char *buf;
+    char buf[32];
 
-    if ((buf = reread_file(file_data)) == NULL) {
+    if (reread_file(file_data, buf, sizeof(buf)) < 0) {
         return -1;
     }
 
@@ -2056,289 +1573,33 @@
         level - 1 : level);
 }
 
-enum zone_watermark {
-    WMARK_MIN = 0,
-    WMARK_LOW,
-    WMARK_HIGH,
-    WMARK_NONE
-};
+static bool is_kill_pending(void) {
+    char buf[24];
 
-struct zone_watermarks {
-    long high_wmark;
-    long low_wmark;
-    long min_wmark;
-};
+    if (last_killed_pid < 0) {
+        return false;
+    }
 
-/*
- * Returns lowest breached watermark or WMARK_NONE.
- */
-static enum zone_watermark get_lowest_watermark(union meminfo *mi,
-                                                struct zone_watermarks *watermarks)
-{
-    int64_t nr_free_pages = mi->field.nr_free_pages - mi->field.cma_free;
+    snprintf(buf, sizeof(buf), "/proc/%d/", last_killed_pid);
+    if (access(buf, F_OK) == 0) {
+        return true;
+    }
 
-    if (nr_free_pages < watermarks->min_wmark) {
-        return WMARK_MIN;
-    }
-    if (nr_free_pages < watermarks->low_wmark) {
-        return WMARK_LOW;
-    }
-    if (nr_free_pages < watermarks->high_wmark) {
-        return WMARK_HIGH;
-    }
-    return WMARK_NONE;
+    // reset last killed PID because there's nothing pending
+    last_killed_pid = -1;
+    return false;
 }
 
-void calc_zone_watermarks(struct zoneinfo *zi, struct zone_watermarks *watermarks) {
-    memset(watermarks, 0, sizeof(struct zone_watermarks));
-
-    for (int node_idx = 0; node_idx < zi->node_count; node_idx++) {
-        struct zoneinfo_node *node = &zi->nodes[node_idx];
-        for (int zone_idx = 0; zone_idx < node->zone_count; zone_idx++) {
-            struct zoneinfo_zone *zone = &node->zones[zone_idx];
-
-            if (!zone->fields.field.present) {
-                continue;
-            }
-
-            watermarks->high_wmark += zone->max_protection + zone->fields.field.high;
-            watermarks->low_wmark += zone->max_protection + zone->fields.field.low;
-            watermarks->min_wmark += zone->max_protection + zone->fields.field.min;
-        }
-    }
-}
-
-static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_params) {
-    enum kill_reasons {
-        NONE = -1, /* To denote no kill condition */
-        PRESSURE_AFTER_KILL = 0,
-        NOT_RESPONDING,
-        LOW_SWAP_AND_THRASHING,
-        LOW_MEM_AND_SWAP,
-        LOW_MEM_AND_THRASHING,
-        DIRECT_RECL_AND_THRASHING,
-        KILL_REASON_COUNT
-    };
-    enum reclaim_state {
-        NO_RECLAIM = 0,
-        KSWAPD_RECLAIM,
-        DIRECT_RECLAIM,
-    };
-    static int64_t init_ws_refault;
-    static int64_t base_file_lru;
-    static int64_t init_pgscan_kswapd;
-    static int64_t init_pgscan_direct;
-    static int64_t swap_low_threshold;
-    static bool killing;
-    static int thrashing_limit;
-    static bool in_reclaim;
-    static struct zone_watermarks watermarks;
-    static struct timespec wmark_update_tm;
-
-    union meminfo mi;
-    union vmstat vs;
-    struct timespec curr_tm;
-    int64_t thrashing = 0;
-    bool swap_is_low = false;
-    enum vmpressure_level level = (enum vmpressure_level)data;
-    enum kill_reasons kill_reason = NONE;
-    bool cycle_after_kill = false;
-    enum reclaim_state reclaim = NO_RECLAIM;
-    enum zone_watermark wmark = WMARK_NONE;
-    char kill_desc[LINE_MAX];
-    bool cut_thrashing_limit = false;
-    int min_score_adj = 0;
-
-    /* Skip while still killing a process */
-    if (is_kill_pending()) {
-        goto no_kill;
-    }
-    /*
-     * Process is dead, stop waiting. This has no effect if pidfds are supported and
-     * death notification already caused waiting to stop.
-     */
-    stop_wait_for_proc_kill(true);
-
-    if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
-        ALOGE("Failed to get current time");
-        return;
-    }
-
-    if (vmstat_parse(&vs) < 0) {
-        ALOGE("Failed to parse vmstat!");
-        return;
-    }
-
-    if (meminfo_parse(&mi) < 0) {
-        ALOGE("Failed to parse meminfo!");
-        return;
-    }
-
-    /* Reset states after process got killed */
-    if (killing) {
-        killing = false;
-        cycle_after_kill = true;
-        /* Reset file-backed pagecache size and refault amounts after a kill */
-        base_file_lru = vs.field.nr_inactive_file + vs.field.nr_active_file;
-        init_ws_refault = vs.field.workingset_refault;
-    }
-
-    /* Check free swap levels */
-    if (swap_free_low_percentage) {
-        if (!swap_low_threshold) {
-            swap_low_threshold = mi.field.total_swap * swap_free_low_percentage / 100;
-        }
-        swap_is_low = mi.field.free_swap < swap_low_threshold;
-    }
-
-    /* Identify reclaim state */
-    if (vs.field.pgscan_direct > init_pgscan_direct) {
-        init_pgscan_direct = vs.field.pgscan_direct;
-        init_pgscan_kswapd = vs.field.pgscan_kswapd;
-        reclaim = DIRECT_RECLAIM;
-    } else if (vs.field.pgscan_kswapd > init_pgscan_kswapd) {
-        init_pgscan_kswapd = vs.field.pgscan_kswapd;
-        reclaim = KSWAPD_RECLAIM;
-    } else {
-        in_reclaim = false;
-        /* Skip if system is not reclaiming */
-        goto no_kill;
-    }
-
-    if (!in_reclaim) {
-        /* Record file-backed pagecache size when entering reclaim cycle */
-        base_file_lru = vs.field.nr_inactive_file + vs.field.nr_active_file;
-        init_ws_refault = vs.field.workingset_refault;
-        thrashing_limit = thrashing_limit_pct;
-    } else {
-        /* Calculate what % of the file-backed pagecache refaulted so far */
-        thrashing = (vs.field.workingset_refault - init_ws_refault) * 100 / base_file_lru;
-    }
-    in_reclaim = true;
-
-    /*
-     * Refresh watermarks once per min in case user updated one of the margins.
-     * TODO: b/140521024 replace this periodic update with an API for AMS to notify LMKD
-     * that zone watermarks were changed by the system software.
-     */
-    if (watermarks.high_wmark == 0 || get_time_diff_ms(&wmark_update_tm, &curr_tm) > 60000) {
-        struct zoneinfo zi;
-
-        if (zoneinfo_parse(&zi) < 0) {
-            ALOGE("Failed to parse zoneinfo!");
-            return;
-        }
-
-        calc_zone_watermarks(&zi, &watermarks);
-        wmark_update_tm = curr_tm;
-     }
-
-    /* Find out which watermark is breached if any */
-    wmark = get_lowest_watermark(&mi, &watermarks);
-
-    /*
-     * TODO: move this logic into a separate function
-     * Decide if killing a process is necessary and record the reason
-     */
-    if (cycle_after_kill && wmark < WMARK_LOW) {
-        /*
-         * Prevent kills not freeing enough memory which might lead to OOM kill.
-         * This might happen when a process is consuming memory faster than reclaim can
-         * free even after a kill. Mostly happens when running memory stress tests.
-         */
-        kill_reason = PRESSURE_AFTER_KILL;
-        strncpy(kill_desc, "min watermark is breached even after kill", sizeof(kill_desc));
-    } else if (level == VMPRESS_LEVEL_CRITICAL && events != 0) {
-        /*
-         * Device is too busy reclaiming memory which might lead to ANR.
-         * Critical level is triggered when PSI complete stall (all tasks are blocked because
-         * of the memory congestion) breaches the configured threshold.
-         */
-        kill_reason = NOT_RESPONDING;
-        strncpy(kill_desc, "device is not responding", sizeof(kill_desc));
-    } else if (swap_is_low && thrashing > thrashing_limit_pct) {
-        /* Page cache is thrashing while swap is low */
-        kill_reason = LOW_SWAP_AND_THRASHING;
-        snprintf(kill_desc, sizeof(kill_desc), "device is low on swap (%" PRId64
-            "kB < %" PRId64 "kB) and thrashing (%" PRId64 "%%)",
-            mi.field.free_swap * page_k, swap_low_threshold * page_k, thrashing);
-    } else if (swap_is_low && wmark < WMARK_HIGH) {
-        /* Both free memory and swap are low */
-        kill_reason = LOW_MEM_AND_SWAP;
-        snprintf(kill_desc, sizeof(kill_desc), "%s watermark is breached and swap is low (%"
-            PRId64 "kB < %" PRId64 "kB)", wmark > WMARK_LOW ? "min" : "low",
-            mi.field.free_swap * page_k, swap_low_threshold * page_k);
-    } else if (wmark < WMARK_HIGH && thrashing > thrashing_limit) {
-        /* Page cache is thrashing while memory is low */
-        kill_reason = LOW_MEM_AND_THRASHING;
-        snprintf(kill_desc, sizeof(kill_desc), "%s watermark is breached and thrashing (%"
-            PRId64 "%%)", wmark > WMARK_LOW ? "min" : "low", thrashing);
-        cut_thrashing_limit = true;
-        /* Do not kill perceptible apps because of thrashing */
-        min_score_adj = PERCEPTIBLE_APP_ADJ;
-    } else if (reclaim == DIRECT_RECLAIM && thrashing > thrashing_limit) {
-        /* Page cache is thrashing while in direct reclaim (mostly happens on lowram devices) */
-        kill_reason = DIRECT_RECL_AND_THRASHING;
-        snprintf(kill_desc, sizeof(kill_desc), "device is in direct reclaim and thrashing (%"
-            PRId64 "%%)", thrashing);
-        cut_thrashing_limit = true;
-        /* Do not kill perceptible apps because of thrashing */
-        min_score_adj = PERCEPTIBLE_APP_ADJ;
-    }
-
-    /* Kill a process if necessary */
-    if (kill_reason != NONE) {
-        int pages_freed = find_and_kill_process(min_score_adj, kill_reason, kill_desc, &mi,
-                                                &curr_tm);
-        if (pages_freed > 0) {
-            killing = true;
-            if (cut_thrashing_limit) {
-                /*
-                 * Cut thrasing limit by thrashing_limit_decay_pct percentage of the current
-                 * thrashing limit until the system stops thrashing.
-                 */
-                thrashing_limit = (thrashing_limit * (100 - thrashing_limit_decay_pct)) / 100;
-            }
-        }
-    }
-
-no_kill:
-    /* Do not poll if kernel supports pidfd waiting */
-    if (is_waiting_for_kill()) {
-        /* Pause polling if we are waiting for process death notification */
-        poll_params->update = POLLING_PAUSE;
-        return;
-    }
-
-    /*
-     * Start polling after initial PSI event;
-     * extend polling while device is in direct reclaim or process is being killed;
-     * do not extend when kswapd reclaims because that might go on for a long time
-     * without causing memory pressure
-     */
-    if (events || killing || reclaim == DIRECT_RECLAIM) {
-        poll_params->update = POLLING_START;
-    }
-
-    /* Decide the polling interval */
-    if (swap_is_low || killing) {
-        /* Fast polling during and after a kill or when swap is low */
-        poll_params->polling_interval_ms = PSI_POLL_PERIOD_SHORT_MS;
-    } else {
-        /* By default use long intervals */
-        poll_params->polling_interval_ms = PSI_POLL_PERIOD_LONG_MS;
-    }
-}
-
-static void mp_event_common(int data, uint32_t events, struct polling_params *poll_params) {
+static void mp_event_common(int data, uint32_t events __unused) {
     int ret;
     unsigned long long evcount;
     int64_t mem_usage, memsw_usage;
     int64_t mem_pressure;
     enum vmpressure_level lvl;
     union meminfo mi;
-    struct zoneinfo zi;
+    union zoneinfo zi;
     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;
@@ -2373,40 +1634,20 @@
         }
     }
 
-    /* Start polling after initial PSI event */
-    if (use_psi_monitors && events) {
-        /* Override polling params only if current event is more critical */
-        if (!poll_params->poll_handler || data > poll_params->poll_handler->data) {
-            poll_params->polling_interval_ms = PSI_POLL_PERIOD_SHORT_MS;
-            poll_params->update = POLLING_START;
-        }
-    }
-
     if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
         ALOGE("Failed to get current time");
         return;
     }
 
-    if (kill_timeout_ms && get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
-        /*
-         * If we're within the no-kill timeout, see if there's pending reclaim work
-         * from the last killed process. If so, skip killing for now.
-         */
-        if (is_kill_pending()) {
+    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
+        // /proc/<pid> continuing to exist), skip killing for now.
+        if ((get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) &&
+            (low_ram_device || is_kill_pending())) {
             kill_skip_count++;
             return;
         }
-        /*
-         * Process is dead, stop waiting. This has no effect if pidfds are supported and
-         * death notification already caused waiting to stop.
-         */
-        stop_wait_for_proc_kill(true);
-    } else {
-        /*
-         * Killing took longer than no-kill timeout. Stop waiting for the last process
-         * to die because we are ready to kill again.
-         */
-        stop_wait_for_proc_kill(false);
     }
 
     if (kill_skip_count > 0) {
@@ -2423,7 +1664,7 @@
     if (use_minfree_levels) {
         int i;
 
-        other_free = mi.field.nr_free_pages - zi.totalreserve_pages;
+        other_free = mi.field.nr_free_pages - zi.field.totalreserve_pages;
         if (mi.field.nr_file_pages > (mi.field.shmem + mi.field.unevictable + mi.field.swap_cached)) {
             other_file = (mi.field.nr_file_pages - mi.field.shmem -
                           mi.field.unevictable - mi.field.swap_cached);
@@ -2505,10 +1746,12 @@
 do_kill:
     if (low_ram_device) {
         /* For Go devices kill only one task */
-        if (find_and_kill_process(level_oomadj[level], -1, NULL, &mi, &curr_tm) == 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;
@@ -2528,7 +1771,7 @@
             min_score_adj = level_oomadj[level];
         }
 
-        pages_freed = find_and_kill_process(min_score_adj, -1, NULL, &mi, &curr_tm);
+        pages_freed = find_and_kill_process(min_score_adj);
 
         if (pages_freed == 0) {
             /* Rate limit kill reports when nothing was reclaimed */
@@ -2536,15 +1779,20 @@
                 report_skip_count++;
                 return;
             }
+        } else {
+            /* If we killed anything, update the last killed timestamp. */
+            last_kill_tm = curr_tm;
         }
 
-        /* Log whenever we kill or when report rate limit allows */
+        /* Log meminfo whenever we kill or when report rate limit allows */
+        meminfo_log(&mi);
+
         if (use_minfree_levels) {
             ALOGI("Reclaimed %ldkB, cache(%ldkB) and "
                 "free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
                 pages_freed * page_k,
                 other_file * page_k, mi.field.nr_free_pages * page_k,
-                zi.totalreserve_pages * page_k,
+                zi.field.totalreserve_pages * page_k,
                 minfree * page_k, min_score_adj);
         } else {
             ALOGI("Reclaimed %ldkB at oom_adj %d",
@@ -2558,21 +1806,10 @@
 
         last_report_tm = curr_tm;
     }
-    if (is_waiting_for_kill()) {
-        /* pause polling if we are waiting for process death notification */
-        poll_params->update = POLLING_PAUSE;
-    }
 }
 
-static bool init_mp_psi(enum vmpressure_level level, bool use_new_strategy) {
-    int fd;
-
-    /* Do not register a handler if threshold_ms is not set */
-    if (!psi_thresholds[level].threshold_ms) {
-        return true;
-    }
-
-    fd = init_psi_monitor(psi_thresholds[level].stall_type,
+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);
 
@@ -2580,7 +1817,7 @@
         return false;
     }
 
-    vmpressure_hinfo[level].handler = use_new_strategy ? mp_event_psi : mp_event_common;
+    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);
@@ -2604,29 +1841,14 @@
 }
 
 static bool init_psi_monitors() {
-    /*
-     * When PSI is used on low-ram devices or on high-end devices without memfree levels
-     * use new kill strategy based on zone watermarks, free swap and thrashing stats
-     */
-    bool use_new_strategy =
-        property_get_bool("ro.lmk.use_new_strategy", low_ram_device || !use_minfree_levels);
-
-    /* In default PSI mode override stall amounts using system properties */
-    if (use_new_strategy) {
-        /* Do not use low pressure level */
-        psi_thresholds[VMPRESS_LEVEL_LOW].threshold_ms = 0;
-        psi_thresholds[VMPRESS_LEVEL_MEDIUM].threshold_ms = psi_partial_stall_ms;
-        psi_thresholds[VMPRESS_LEVEL_CRITICAL].threshold_ms = psi_complete_stall_ms;
-    }
-
-    if (!init_mp_psi(VMPRESS_LEVEL_LOW, use_new_strategy)) {
+    if (!init_mp_psi(VMPRESS_LEVEL_LOW)) {
         return false;
     }
-    if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM, use_new_strategy)) {
+    if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM)) {
         destroy_mp_psi(VMPRESS_LEVEL_LOW);
         return false;
     }
-    if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL, use_new_strategy)) {
+    if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL)) {
         destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
         destroy_mp_psi(VMPRESS_LEVEL_LOW);
         return false;
@@ -2701,19 +1923,8 @@
     return false;
 }
 
-static void kernel_event_handler(int data __unused, uint32_t events __unused,
-                                 struct polling_params *poll_params __unused) {
-    kpoll_info.handler(kpoll_info.poll_fd);
-}
-
 static int init(void) {
-    static struct event_handler_info kernel_poll_hinfo = { 0, kernel_event_handler };
-    struct reread_data file_data = {
-        .filename = ZONEINFO_PATH,
-        .fd = -1,
-    };
     struct epoll_event epev;
-    int pidfd;
     int i;
     int ret;
 
@@ -2759,17 +1970,6 @@
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
-        if (init_poll_kernel(&kpoll_info)) {
-            epev.events = EPOLLIN;
-            epev.data.ptr = (void*)&kernel_poll_hinfo;
-            if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kpoll_info.poll_fd, &epev) != 0) {
-                ALOGE("epoll_ctl for lmk events failed (errno=%d)", errno);
-                close(kpoll_info.poll_fd);
-                kpoll_info.poll_fd = -1;
-            } else {
-                maxevents++;
-            }
-        }
     } else {
         /* Try to use psi monitor first if kernel has it */
         use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
@@ -2796,110 +1996,37 @@
 
     memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx));
 
-    /*
-     * Read zoneinfo as the biggest file we read to create and size the initial
-     * read buffer and avoid memory re-allocations during memory pressure
-     */
-    if (reread_file(&file_data) == NULL) {
-        ALOGE("Failed to read %s: %s", file_data.filename, strerror(errno));
-    }
-
-    /* check if kernel supports pidfd_open syscall */
-    pidfd = TEMP_FAILURE_RETRY(sys_pidfd_open(getpid(), 0));
-    if (pidfd < 0) {
-        pidfd_supported = (errno != ENOSYS);
-    } else {
-        pidfd_supported = true;
-        close(pidfd);
-    }
-    ALOGI("Process polling is %s", pidfd_supported ? "supported" : "not supported" );
-
     return 0;
 }
 
-static void call_handler(struct event_handler_info* handler_info,
-                         struct polling_params *poll_params, uint32_t events) {
-    struct timespec curr_tm;
-
-    handler_info->handler(handler_info->data, events, poll_params);
-    clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
-    poll_params->last_poll_tm = curr_tm;
-
-    switch (poll_params->update) {
-    case POLLING_START:
-        /*
-         * Poll for the duration of PSI_WINDOW_SIZE_MS after the
-         * initial PSI event because psi events are rate-limited
-         * at one per sec.
-         */
-        poll_params->poll_start_tm = curr_tm;
-        poll_params->poll_handler = handler_info;
-        break;
-    case POLLING_STOP:
-        poll_params->poll_handler = NULL;
-        break;
-    case POLLING_PAUSE:
-        poll_params->paused_handler = handler_info;
-        poll_params->poll_handler = NULL;
-        break;
-    case POLLING_RESUME:
-        poll_params->poll_start_tm = curr_tm;
-        poll_params->poll_handler = poll_params->paused_handler;
-        break;
-    case POLLING_DO_NOT_CHANGE:
-        if (get_time_diff_ms(&poll_params->poll_start_tm, &curr_tm) > PSI_WINDOW_SIZE_MS) {
-            /* Polled for the duration of PSI window, time to stop */
-            poll_params->poll_handler = NULL;
-        }
-        /* WARNING: skipping the rest of the function */
-        return;
-    }
-    poll_params->update = POLLING_DO_NOT_CHANGE;
-}
-
 static void mainloop(void) {
     struct event_handler_info* handler_info;
-    struct polling_params poll_params;
-    struct timespec curr_tm;
+    struct event_handler_info* poll_handler = NULL;
+    struct timespec last_report_tm, curr_tm;
     struct epoll_event *evt;
     long delay = -1;
-
-    poll_params.poll_handler = NULL;
-    poll_params.update = POLLING_DO_NOT_CHANGE;
+    int polling = 0;
 
     while (1) {
         struct epoll_event events[maxevents];
         int nevents;
         int i;
 
-        if (poll_params.poll_handler) {
-            bool poll_now;
+        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 (poll_params.poll_handler == poll_params.paused_handler) {
-                /*
-                 * Just transitioned into POLLING_RESUME. Reset paused_handler
-                 * and poll immediately
-                 */
-                poll_params.paused_handler = NULL;
-                poll_now = true;
-                nevents = 0;
-            } else {
-                /* Calculate next timeout */
-                delay = get_time_diff_ms(&poll_params.last_poll_tm, &curr_tm);
-                delay = (delay < poll_params.polling_interval_ms) ?
-                    poll_params.polling_interval_ms - delay : poll_params.polling_interval_ms;
-
-                /* Wait for events until the next polling timeout */
-                nevents = epoll_wait(epollfd, events, maxevents, delay);
-
-                /* Update current time after wait */
-                clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
-                poll_now = (get_time_diff_ms(&poll_params.last_poll_tm, &curr_tm) >=
-                    poll_params.polling_interval_ms);
-            }
-            if (poll_now) {
-                call_handler(poll_params.poll_handler, &poll_params, 0);
+            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 */
@@ -2930,16 +2057,26 @@
 
         /* Second pass to handle all other events */
         for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
-            if (evt->events & EPOLLERR) {
+            if (evt->events & EPOLLERR)
                 ALOGD("EPOLLERR on event #%d", i);
-            }
             if (evt->events & EPOLLHUP) {
                 /* This case was handled in the first pass */
                 continue;
             }
             if (evt->data.ptr) {
                 handler_info = (struct event_handler_info*)evt->data.ptr;
-                call_handler(handler_info, &poll_params, evt->events);
+                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);
+                }
             }
         }
     }
@@ -2975,20 +2112,14 @@
         property_get_bool("ro.lmk.use_minfree_levels", false);
     per_app_memcg =
         property_get_bool("ro.config.per_app_memcg", low_ram_device);
-    swap_free_low_percentage = clamp(0, 100, property_get_int32("ro.lmk.swap_free_low_percentage",
-        low_ram_device ? DEF_LOW_SWAP_LOWRAM : DEF_LOW_SWAP));
-    psi_partial_stall_ms = property_get_int32("ro.lmk.psi_partial_stall_ms",
-        low_ram_device ? DEF_PARTIAL_STALL_LOWRAM : DEF_PARTIAL_STALL);
-    psi_complete_stall_ms = property_get_int32("ro.lmk.psi_complete_stall_ms",
-        DEF_COMPLETE_STALL);
-    thrashing_limit_pct = max(0, property_get_int32("ro.lmk.thrashing_limit",
-        low_ram_device ? DEF_THRASHING_LOWRAM : DEF_THRASHING));
-    thrashing_limit_decay_pct = clamp(0, 100, property_get_int32("ro.lmk.thrashing_limit_decay",
-        low_ram_device ? DEF_THRASHING_DECAY_LOWRAM : DEF_THRASHING_DECAY));
+    swap_free_low_percentage =
+        property_get_int32("ro.lmk.swap_free_low_percentage", 10);
 
-    ctx = create_android_logger(KILLINFO_LOG_TAG);
+    ctx = create_android_logger(MEMINFO_LOG_TAG);
 
-    statslog_init();
+#ifdef LMKD_LOG_STATS
+    statslog_init(&log_ctx, &enable_stats_log);
+#endif
 
     if (!init()) {
         if (!use_inkernel_interface) {
@@ -3017,7 +2148,9 @@
         mainloop();
     }
 
-    statslog_destroy();
+#ifdef LMKD_LOG_STATS
+    statslog_destroy(&log_ctx);
+#endif
 
     android_log_destroy(&ctx);
 
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
index 982a188..76b6055 100644
--- a/lmkd/lmkd.rc
+++ b/lmkd/lmkd.rc
@@ -4,5 +4,5 @@
     group lmkd system readproc
     capabilities DAC_OVERRIDE KILL IPC_LOCK SYS_NICE SYS_RESOURCE
     critical
-    socket lmkd seqpacket+passcred 0660 system system
+    socket lmkd seqpacket 0660 system system
     writepid /dev/cpuset/system-background/tasks
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index c0fd6df..0c230ae 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -18,32 +18,8 @@
 #include <errno.h>
 #include <log/log_id.h>
 #include <stats_event_list.h>
-#include <statslog.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdlib.h>
-#include <string.h>
 #include <time.h>
 
-#ifdef LMKD_LOG_STATS
-
-#define LINE_MAX 128
-#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
-#define STRINGIFY_INTERNAL(x) #x
-
-static bool enable_stats_log;
-static android_log_context log_ctx;
-
-struct proc {
-    int pid;
-    char taskname[LINE_MAX];
-    struct proc* pidhash_next;
-};
-
-#define PIDHASH_SZ 1024
-static struct proc** pidhash = NULL;
-#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
-
 static int64_t getElapsedRealTimeNs() {
     struct timespec t;
     t.tv_sec = t.tv_nsec = 0;
@@ -51,64 +27,34 @@
     return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
 }
 
-void statslog_init() {
-    enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
-
-    if (enable_stats_log) {
-        log_ctx = create_android_logger(kStatsEventTag);
-    }
-}
-
-void statslog_destroy() {
-    if (log_ctx) {
-        android_log_destroy(&log_ctx);
-    }
-}
-
 /**
  * Logs the change in LMKD state which is used as start/stop boundaries for logging
  * LMK_KILL_OCCURRED event.
  * Code: LMK_STATE_CHANGED = 54
  */
 int
-stats_write_lmk_state_changed(int32_t code, int32_t state) {
+stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state) {
+    assert(ctx != NULL);
     int ret = -EINVAL;
-
-    if (!enable_stats_log) {
+    if (!ctx) {
         return ret;
     }
 
-    assert(log_ctx != NULL);
-    if (!log_ctx) {
+    reset_log_context(ctx);
+
+    if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
         return ret;
     }
 
-    reset_log_context(log_ctx);
-
-    if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
+    if ((ret = android_log_write_int32(ctx, code)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
+    if ((ret = android_log_write_int32(ctx, state)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(log_ctx, state)) < 0) {
-        return ret;
-    }
-
-    return write_to_logger(log_ctx, LOG_ID_STATS);
-}
-
-static struct proc* pid_lookup(int pid) {
-    struct proc* procp;
-
-    if (!pidhash) return NULL;
-
-    for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; procp = procp->pidhash_next)
-        ;
-
-    return procp;
+    return write_to_logger(ctx, LOG_ID_STATS);
 }
 
 /**
@@ -116,317 +62,65 @@
  * Code: LMK_KILL_OCCURRED = 51
  */
 int
-stats_write_lmk_kill_occurred(int32_t code, int32_t uid, char const* process_name,
-                              int32_t oom_score, int32_t min_oom_score, int tasksize,
-                              struct memory_stat *mem_st) {
+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 process_start_time_ns,
+                              int32_t min_oom_score) {
+    assert(ctx != NULL);
     int ret = -EINVAL;
-    if (!enable_stats_log) {
+    if (!ctx) {
         return ret;
     }
-    if (!log_ctx) {
-        return ret;
-    }
-    reset_log_context(log_ctx);
+    reset_log_context(ctx);
 
-    if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
+    if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
+    if ((ret = android_log_write_int32(ctx, code)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(log_ctx, uid)) < 0) {
+    if ((ret = android_log_write_int32(ctx, uid)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_string8(log_ctx, (process_name == NULL) ? "" : process_name)) < 0) {
+    if ((ret = android_log_write_string8(ctx, (process_name == NULL) ? "" : process_name)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(log_ctx, oom_score)) < 0) {
+    if ((ret = android_log_write_int32(ctx, oom_score)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgfault : -1)) < 0) {
+    if ((ret = android_log_write_int64(ctx, pgfault)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgmajfault : -1)) < 0) {
+    if ((ret = android_log_write_int64(ctx, pgmajfault)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->rss_in_bytes
-                                                       : tasksize * BYTES_IN_KILOBYTE)) < 0) {
+    if ((ret = android_log_write_int64(ctx, rss_in_bytes)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->cache_in_bytes : -1)) < 0) {
+    if ((ret = android_log_write_int64(ctx, cache_in_bytes)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->swap_in_bytes : -1)) < 0) {
+    if ((ret = android_log_write_int64(ctx, swap_in_bytes)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->process_start_time_ns
-                                                       : -1)) < 0) {
+    if ((ret = android_log_write_int64(ctx, process_start_time_ns)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(log_ctx, min_oom_score)) < 0) {
+    if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
         return ret;
     }
 
-    return write_to_logger(log_ctx, LOG_ID_STATS);
+    return write_to_logger(ctx, LOG_ID_STATS);
 }
-
-static int stats_write_lmk_kill_occurred_pid(int32_t code, int32_t uid, int pid,
-                                      int32_t oom_score, int32_t min_oom_score, int tasksize,
-                                      struct memory_stat *mem_st) {
-    struct proc* proc = pid_lookup(pid);
-    if (!proc) return -EINVAL;
-
-    return stats_write_lmk_kill_occurred(code, uid, proc->taskname, oom_score, min_oom_score,
-                                         tasksize, mem_st);
-}
-
-static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
-    char key[LINE_MAX + 1];
-    int64_t value;
-
-    sscanf(line, "%" STRINGIFY(LINE_MAX) "s  %" SCNd64 "", key, &value);
-
-    if (strcmp(key, "total_") < 0) {
-        return;
-    }
-
-    if (!strcmp(key, "total_pgfault"))
-        mem_st->pgfault = value;
-    else if (!strcmp(key, "total_pgmajfault"))
-        mem_st->pgmajfault = value;
-    else if (!strcmp(key, "total_rss"))
-        mem_st->rss_in_bytes = value;
-    else if (!strcmp(key, "total_cache"))
-        mem_st->cache_in_bytes = value;
-    else if (!strcmp(key, "total_swap"))
-        mem_st->swap_in_bytes = value;
-}
-
-static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
-    FILE *fp;
-    char buf[PATH_MAX];
-
-    snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
-
-    fp = fopen(buf, "r");
-
-    if (fp == NULL) {
-        return -1;
-    }
-
-    while (fgets(buf, PAGE_SIZE, fp) != NULL) {
-        memory_stat_parse_line(buf, mem_st);
-    }
-    fclose(fp);
-
-    return 0;
-}
-
-static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
-    char path[PATH_MAX];
-    char buffer[PROC_STAT_BUFFER_SIZE];
-    int fd, ret;
-
-    snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
-    if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
-        return -1;
-    }
-
-    ret = read(fd, buffer, sizeof(buffer));
-    if (ret < 0) {
-        close(fd);
-        return -1;
-    }
-    close(fd);
-
-    // field 10 is pgfault
-    // field 12 is pgmajfault
-    // field 22 is starttime
-    // field 24 is rss_in_pages
-    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 "
-               "%" 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;
-}
-
-struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid) {
-    static struct memory_stat mem_st = {};
-
-    if (!enable_stats_log) {
-        return NULL;
-    }
-
-    if (per_app_memcg) {
-        if (memory_stat_from_cgroup(&mem_st, pid, uid) == 0) {
-            return &mem_st;
-        }
-    } else {
-        if (memory_stat_from_procfs(&mem_st, pid) == 0) {
-            return &mem_st;
-        }
-    }
-
-    return NULL;
-}
-
-static void poll_kernel(int poll_fd) {
-    if (poll_fd == -1) {
-        // not waiting
-        return;
-    }
-
-    while (1) {
-        char rd_buf[256];
-        int bytes_read =
-                TEMP_FAILURE_RETRY(pread(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 rss_in_pages;
-        struct memory_stat mem_st = {};
-        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, &mem_st.pgfault,
-                                 &mem_st.pgmajfault, &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) {
-            mem_st.process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
-            mem_st.rss_in_bytes = rss_in_pages * PAGE_SIZE;
-            stats_write_lmk_kill_occurred_pid(LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
-                                              min_score_adj, 0, &mem_st);
-        }
-
-        free(taskname);
-    }
-}
-
-bool init_poll_kernel(struct kernel_poll_info *poll_info) {
-    if (!enable_stats_log) {
-        return false;
-    }
-
-    poll_info->poll_fd =
-            TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
-
-    if (poll_info->poll_fd < 0) {
-        ALOGE("kernel lmk event file could not be opened; errno=%d", errno);
-        return false;
-    }
-    poll_info->handler = poll_kernel;
-
-    return true;
-}
-
-static void proc_insert(struct proc* procp) {
-    if (!pidhash) {
-        pidhash = calloc(PIDHASH_SZ, sizeof(struct proc));
-    }
-
-    int hval = pid_hashfn(procp->pid);
-    procp->pidhash_next = pidhash[hval];
-    pidhash[hval] = procp;
-}
-
-void stats_remove_taskname(int pid, int poll_fd) {
-    if (!enable_stats_log || !pidhash) {
-        return;
-    }
-
-    /*
-     * Perform an extra check before the pid is removed, after which it
-     * will be impossible for poll_kernel to get the taskname. poll_kernel()
-     * is potentially a long-running blocking function; however this method
-     * handles AMS requests but does not block AMS.
-     */
-    poll_kernel(poll_fd);
-
-    int hval = pid_hashfn(pid);
-    struct proc* procp;
-    struct proc* prevp;
-
-    for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
-         procp = procp->pidhash_next)
-        prevp = procp;
-
-    if (!procp)
-        return;
-
-    if (!prevp)
-        pidhash[hval] = procp->pidhash_next;
-    else
-        prevp->pidhash_next = procp->pidhash_next;
-
-    free(procp);
-}
-
-void stats_store_taskname(int pid, const char* taskname, int poll_fd) {
-    if (!enable_stats_log) {
-        return;
-    }
-
-    struct proc* procp = pid_lookup(pid);
-    if (procp != NULL) {
-        if (strcmp(procp->taskname, taskname) == 0) {
-            return;
-        }
-        stats_remove_taskname(pid, poll_fd);
-    }
-    procp = malloc(sizeof(struct proc));
-    procp->pid = pid;
-    strncpy(procp->taskname, taskname, LINE_MAX - 1);
-    procp->taskname[LINE_MAX - 1] = '\0';
-    proc_insert(procp);
-}
-
-void stats_purge_tasknames() {
-    if (!enable_stats_log || !pidhash) {
-        return;
-    }
-
-    struct proc* procp;
-    struct proc* next;
-    int i;
-    for (i = 0; i < PIDHASH_SZ; i++) {
-        procp = pidhash[i];
-        while (procp) {
-            next = procp->pidhash_next;
-            free(procp);
-            procp = next;
-        }
-    }
-    memset(pidhash, 0, PIDHASH_SZ * sizeof(struct proc));
-}
-
-#endif /* LMKD_LOG_STATS */
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index a09c7dd..2edba7a 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -18,7 +18,6 @@
 #define _STATSLOG_H_
 
 #include <assert.h>
-#include <inttypes.h>
 #include <stats_event_list.h>
 #include <stdbool.h>
 #include <sys/cdefs.h>
@@ -27,20 +26,6 @@
 
 __BEGIN_DECLS
 
-struct memory_stat {
-    int64_t pgfault;
-    int64_t pgmajfault;
-    int64_t rss_in_bytes;
-    int64_t cache_in_bytes;
-    int64_t swap_in_bytes;
-    int64_t process_start_time_ns;
-};
-
-struct kernel_poll_info {
-    int poll_fd;
-    void (*handler)(int poll_fd);
-};
-
 /*
  * These are defined in
  * http://cs/android/frameworks/base/cmds/statsd/src/atoms.proto
@@ -50,17 +35,37 @@
 #define LMK_STATE_CHANGE_START 1
 #define LMK_STATE_CHANGE_STOP 2
 
-#ifdef LMKD_LOG_STATS
-
 /*
  * The single event tag id for all stats logs.
  * Keep this in sync with system/core/logcat/event.logtags
  */
 const static int kStatsEventTag = 1937006964;
 
-void statslog_init();
+static inline void statslog_init(android_log_context* log_ctx, bool* enable_stats_log) {
+    assert(log_ctx != NULL);
+    assert(enable_stats_log != NULL);
+    *enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
 
-void statslog_destroy();
+    if (*enable_stats_log) {
+        *log_ctx = create_android_logger(kStatsEventTag);
+    }
+}
+
+static inline void statslog_destroy(android_log_context* log_ctx) {
+    assert(log_ctx != NULL);
+    if (*log_ctx) {
+        android_log_destroy(log_ctx);
+    }
+}
+
+struct memory_stat {
+    int64_t pgfault;
+    int64_t pgmajfault;
+    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"
 #define PROC_STAT_FILE_PATH "/proc/%d/stat"
@@ -73,63 +78,18 @@
  * Code: LMK_STATE_CHANGED = 54
  */
 int
-stats_write_lmk_state_changed(int32_t code, int32_t state);
+stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state);
 
 /**
  * Logs the event when LMKD kills a process to reduce memory pressure.
  * Code: LMK_KILL_OCCURRED = 51
  */
 int
-stats_write_lmk_kill_occurred(int32_t code, int32_t uid,
-                              char const* process_name, int32_t oom_score, int32_t min_oom_score,
-                              int tasksize, struct memory_stat *mem_st);
-
-struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid);
-
-/**
- * Registers a process taskname by pid, while it is still alive.
- */
-void stats_store_taskname(int pid, const char* taskname, int poll_fd);
-
-/**
- * Unregister all process tasknames.
- */
-void stats_purge_tasknames();
-
-/**
- * Unregister a process taskname, e.g. after it has been killed.
- */
-void stats_remove_taskname(int pid, int poll_fd);
-
-bool init_poll_kernel(struct kernel_poll_info *poll_info);
-
-#else /* LMKD_LOG_STATS */
-
-static inline void statslog_init() {}
-static inline void statslog_destroy() {}
-
-static inline int
-stats_write_lmk_state_changed(int32_t code __unused, int32_t state __unused) { return -EINVAL; }
-
-static inline int
-stats_write_lmk_kill_occurred(int32_t code __unused, int32_t uid __unused,
-                              char const* process_name __unused, int32_t oom_score __unused,
-                              int32_t min_oom_score __unused, int tasksize __unused,
-                              struct memory_stat *mem_st __unused) { return -EINVAL; }
-
-static inline struct memory_stat *stats_read_memory_stat(bool per_app_memcg __unused,
-                                    int pid __unused, uid_t uid __unused) { return NULL; }
-
-static inline void stats_store_taskname(int pid __unused, const char* taskname __unused,
-                                        int poll_fd __unused) {}
-
-static inline void stats_purge_tasknames() {}
-
-static inline void stats_remove_taskname(int pid __unused, int poll_fd __unused) {}
-
-static inline bool init_poll_kernel(struct kernel_poll_info *poll_info __unused) { return false; }
-
-#endif /* LMKD_LOG_STATS */
+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 process_start_time_ns,
+                              int32_t min_oom_score);
 
 __END_DECLS
 
diff --git a/logcat/Android.bp b/logcat/Android.bp
index 61fba59..5030b15 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -21,10 +21,10 @@
         "-Wall",
         "-Wextra",
         "-Werror",
-        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
     ],
     shared_libs: [
         "libbase",
+        "libpcrecpp",
         "libprocessgroup",
     ],
     static_libs: ["liblog"],
@@ -36,22 +36,31 @@
 
     defaults: ["logcat_defaults"],
     srcs: [
+        "logcat_main.cpp",
         "logcat.cpp",
     ],
 }
 
-sh_binary {
+cc_binary {
     name: "logcatd",
-    src: "logcatd",
+
+    defaults: ["logcat_defaults"],
+    srcs: [
+        "logcatd_main.cpp",
+        "logcat.cpp",
+    ],
 }
 
-sh_binary {
+cc_prebuilt_binary {
     name: "logpersist.start",
-    src: "logpersist",
+    srcs: ["logpersist"],
     init_rc: ["logcatd.rc"],
     required: ["logcatd"],
     symlinks: [
         "logpersist.stop",
         "logpersist.cat",
     ],
+    strip: {
+        none: true,
+    },
 }
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 70ccb80..15e07fe 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,145 +14,255 @@
  * 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>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/cdefs.h>
-#include <sys/ioctl.h>
 #include <sys/resource.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <memory>
-#include <regex>
 #include <string>
 #include <utility>
 #include <vector>
 
 #include <android-base/file.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/unique_fd.h>
-#include <android/log.h>
+#include <cutils/sockets.h>
 #include <log/event_tag_map.h>
-#include <log/log_id.h>
 #include <log/logprint.h>
 #include <private/android_logger.h>
 #include <processgroup/sched_policy.h>
 #include <system/thread_defs.h>
 
+#include <pcrecpp.h>
+
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
-using android::base::Join;
-using android::base::ParseByteCount;
-using android::base::ParseUint;
-using android::base::Split;
-using android::base::StringPrintf;
+struct log_device_t {
+    const char* device;
+    bool binary;
+    struct logger* logger;
+    struct logger_list* logger_list;
+    bool printed;
 
-class Logcat {
-  public:
-    int Run(int argc, char** argv);
+    log_device_t* next;
 
-  private:
-    void RotateLogs();
-    void ProcessBuffer(struct log_msg* buf);
-    void PrintDividers(log_id_t log_id, bool print_dividers);
-    void SetupOutputAndSchedulingPolicy(bool blocking);
-    int SetLogFormat(const char* format_string);
-
-    // Used for all options
-    android::base::unique_fd output_fd_{dup(STDOUT_FILENO)};
-    std::unique_ptr<AndroidLogFormat, decltype(&android_log_format_free)> logformat_{
-            android_log_format_new(), &android_log_format_free};
-
-    // For logging to a file and log rotation
-    const char* output_file_name_ = nullptr;
-    size_t log_rotate_size_kb_ = 0;                       // 0 means "no log rotation"
-    size_t max_rotated_logs_ = DEFAULT_MAX_ROTATED_LOGS;  // 0 means "unbounded"
-    size_t out_byte_count_ = 0;
-
-    // For binary log buffers
-    int print_binary_ = 0;
-    std::unique_ptr<EventTagMap, decltype(&android_closeEventTagMap)> event_tag_map_{
-            nullptr, &android_closeEventTagMap};
-    bool has_opened_event_tag_map_ = false;
-
-    // For the related --regex, --max-count, --print
-    std::unique_ptr<std::regex> regex_;
-    size_t max_count_ = 0;  // 0 means "infinite"
-    size_t print_count_ = 0;
-    bool print_it_anyways_ = false;
-
-    // For PrintDividers()
-    log_id_t last_printed_id_ = LOG_ID_MAX;
-    bool printed_start_[LOG_ID_MAX] = {};
-
-    bool debug_ = false;
+    log_device_t(const char* d, bool b) {
+        device = d;
+        binary = b;
+        next = nullptr;
+        printed = false;
+        logger = nullptr;
+        logger_list = nullptr;
+    }
 };
 
+struct android_logcat_context_internal {
+    // status
+    volatile std::atomic_int retval;  // valid if thread_stopped set
+    // Arguments passed in, or copies and storage thereof if a thread.
+    int argc;
+    char* const* argv;
+    char* const* envp;
+    std::vector<std::string> args;
+    std::vector<const char*> argv_hold;
+    std::vector<std::string> envs;
+    std::vector<const char*> envp_hold;
+    int output_fd;  // duplication of fileno(output) (below)
+    int error_fd;   // duplication of fileno(error) (below)
+
+    // library
+    int fds[2];    // From popen call
+    FILE* output;  // everything writes to fileno(output), buffer unused
+    FILE* error;   // unless error == output.
+    pthread_t thr;
+    volatile std::atomic_bool stop;  // quick exit flag
+    volatile std::atomic_bool thread_stopped;
+    bool stderr_null;    // shell "2>/dev/null"
+    bool stderr_stdout;  // shell "2>&1"
+
+    // global variables
+    AndroidLogFormat* logformat;
+    const char* outputFileName;
+    // 0 means "no log rotation"
+    size_t logRotateSizeKBytes;
+    // 0 means "unbounded"
+    size_t maxRotatedLogs;
+    size_t outByteCount;
+    int printBinary;
+    int devCount;  // >1 means multiple
+    pcrecpp::RE* regex;
+    log_device_t* devices;
+    EventTagMap* eventTagMap;
+    // 0 means "infinite"
+    size_t maxCount;
+    size_t printCount;
+
+    bool printItAnyways;
+    bool debug;
+    bool hasOpenedEventTagMap;
+};
+
+// Creates a context associated with this logcat instance
+android_logcat_context create_android_logcat() {
+    android_logcat_context_internal* context;
+
+    context = (android_logcat_context_internal*)calloc(
+        1, sizeof(android_logcat_context_internal));
+    if (!context) return nullptr;
+
+    context->fds[0] = -1;
+    context->fds[1] = -1;
+    context->output_fd = -1;
+    context->error_fd = -1;
+    context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
+
+    context->argv_hold.clear();
+    context->args.clear();
+    context->envp_hold.clear();
+    context->envs.clear();
+
+    return (android_logcat_context)context;
+}
+
 // logd prefixes records with a length field
 #define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)
 
+namespace android {
+
 enum helpType { HELP_FALSE, HELP_TRUE, HELP_FORMAT };
 
-// if show_help is set, newline required in fmt statement to transition to usage
-static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) __printflike(2, 3)
-        __attribute__((__noreturn__));
+// if showHelp is set, newline required in fmt statement to transition to usage
+static void logcat_panic(android_logcat_context_internal* context,
+                         enum helpType showHelp, const char* fmt, ...)
+    __printflike(3, 4);
 
-#ifndef F2FS_IOC_SET_PIN_FILE
-#define F2FS_IOCTL_MAGIC       0xf5
-#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
-#endif
-
-static int openLogFile(const char* pathname, size_t sizeKB) {
-    int fd = open(pathname, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
-    if (fd < 0) {
-        return fd;
-    }
-
-    // no need to check errors
-    __u32 set = 1;
-    ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
-    fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, (sizeKB << 10));
-    return fd;
+static int openLogFile(const char* pathname) {
+    return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
 }
 
-void Logcat::RotateLogs() {
-    // Can't rotate logs if we're not outputting to a file
-    if (!output_file_name_) return;
+static void close_output(android_logcat_context_internal* context) {
+    // split output_from_error
+    if (context->error == context->output) {
+        context->output = nullptr;
+        context->output_fd = -1;
+    }
+    if (context->error && (context->output_fd == fileno(context->error))) {
+        context->output_fd = -1;
+    }
+    if (context->output_fd == context->error_fd) {
+        context->output_fd = -1;
+    }
+    // close output channel
+    if (context->output) {
+        if (context->output != stdout) {
+            if (context->output_fd == fileno(context->output)) {
+                context->output_fd = -1;
+            }
+            if (context->fds[1] == fileno(context->output)) {
+                context->fds[1] = -1;
+            }
+            fclose(context->output);
+        }
+        context->output = nullptr;
+    }
+    if (context->output_fd >= 0) {
+        if (context->output_fd != fileno(stdout)) {
+            if (context->fds[1] == context->output_fd) {
+                context->fds[1] = -1;
+            }
+            close(context->output_fd);
+        }
+        context->output_fd = -1;
+    }
+}
 
-    output_fd_.reset();
+static void close_error(android_logcat_context_internal* context) {
+    // split error_from_output
+    if (context->output == context->error) {
+        context->error = nullptr;
+        context->error_fd = -1;
+    }
+    if (context->output && (context->error_fd == fileno(context->output))) {
+        context->error_fd = -1;
+    }
+    if (context->error_fd == context->output_fd) {
+        context->error_fd = -1;
+    }
+    // close error channel
+    if (context->error) {
+        if ((context->error != stderr) && (context->error != stdout)) {
+            if (context->error_fd == fileno(context->error)) {
+                context->error_fd = -1;
+            }
+            if (context->fds[1] == fileno(context->error)) {
+                context->fds[1] = -1;
+            }
+            fclose(context->error);
+        }
+        context->error = nullptr;
+    }
+    if (context->error_fd >= 0) {
+        if ((context->error_fd != fileno(stdout)) &&
+            (context->error_fd != fileno(stderr))) {
+            if (context->fds[1] == context->error_fd) context->fds[1] = -1;
+            close(context->error_fd);
+        }
+        context->error_fd = -1;
+    }
+}
+
+static void rotateLogs(android_logcat_context_internal* context) {
+    int err;
+
+    // Can't rotate logs if we're not outputting to a file
+    if (!context->outputFileName) return;
+
+    close_output(context);
 
     // Compute the maximum number of digits needed to count up to
     // maxRotatedLogs in decimal.  eg:
     // maxRotatedLogs == 30
     //   -> log10(30) == 1.477
     //   -> maxRotationCountDigits == 2
-    int max_rotation_count_digits =
-            max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
+    int maxRotationCountDigits =
+        (context->maxRotatedLogs > 0)
+            ? (int)(floor(log10(context->maxRotatedLogs) + 1))
+            : 0;
 
-    for (int i = max_rotated_logs_; i > 0; i--) {
-        std::string file1 =
-                StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i);
+    for (int i = context->maxRotatedLogs; i > 0; i--) {
+        std::string file1 = android::base::StringPrintf(
+            "%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
 
         std::string file0;
         if (!(i - 1)) {
-            file0 = output_file_name_;
+            file0 = android::base::StringPrintf("%s", context->outputFileName);
         } else {
-            file0 = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits, i - 1);
+            file0 =
+                android::base::StringPrintf("%s.%.*d", context->outputFileName,
+                                            maxRotationCountDigits, i - 1);
         }
 
         if (!file0.length() || !file1.length()) {
@@ -160,129 +270,172 @@
             break;
         }
 
-        int err = rename(file0.c_str(), file1.c_str());
+        err = rename(file0.c_str(), file1.c_str());
 
         if (err < 0 && errno != ENOENT) {
             perror("while rotating log files");
         }
     }
 
-    output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
+    context->output_fd = openLogFile(context->outputFileName);
 
-    if (!output_fd_.ok()) {
-        LogcatPanic(HELP_FALSE, "couldn't open output file");
+    if (context->output_fd < 0) {
+        logcat_panic(context, HELP_FALSE, "couldn't open output file");
+        return;
+    }
+    context->output = fdopen(context->output_fd, "web");
+    if (!context->output) {
+        logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
+        return;
+    }
+    if (context->stderr_stdout) {
+        close_error(context);
+        context->error = context->output;
+        context->error_fd = context->output_fd;
     }
 
-    out_byte_count_ = 0;
+    context->outByteCount = 0;
 }
 
-void Logcat::ProcessBuffer(struct log_msg* buf) {
+void printBinary(android_logcat_context_internal* context, struct log_msg* buf) {
+    size_t size = buf->len();
+
+    TEMP_FAILURE_RETRY(write(context->output_fd, buf, size));
+}
+
+static bool regexOk(android_logcat_context_internal* context,
+                    const AndroidLogEntry& entry) {
+    if (!context->regex) return true;
+
+    std::string messageString(entry.message, entry.messageLen);
+
+    return context->regex->PartialMatch(messageString);
+}
+
+static void processBuffer(android_logcat_context_internal* context,
+                          log_device_t* dev, struct log_msg* buf) {
     int bytesWritten = 0;
     int err;
     AndroidLogEntry entry;
     char binaryMsgBuf[1024];
 
-    bool is_binary =
-            buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY;
-
-    if (is_binary) {
-        if (!event_tag_map_ && !has_opened_event_tag_map_) {
-            event_tag_map_.reset(android_openEventTagMap(nullptr));
-            has_opened_event_tag_map_ = true;
+    if (dev->binary) {
+        if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
+            context->eventTagMap = android_openEventTagMap(nullptr);
+            context->hasOpenedEventTagMap = true;
         }
-        err = android_log_processBinaryLogBuffer(&buf->entry, &entry, event_tag_map_.get(),
-                                                 binaryMsgBuf, sizeof(binaryMsgBuf));
+        err = android_log_processBinaryLogBuffer(
+            &buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
+            sizeof(binaryMsgBuf));
         // printf(">>> pri=%d len=%d msg='%s'\n",
         //    entry.priority, entry.messageLen, entry.message);
     } else {
-        err = android_log_processLogBuffer(&buf->entry, &entry);
+        err = android_log_processLogBuffer(&buf->entry_v1, &entry);
     }
-    if (err < 0 && !debug_) return;
+    if ((err < 0) && !context->debug) return;
 
-    if (android_log_shouldPrintLine(logformat_.get(), std::string(entry.tag, entry.tagLen).c_str(),
-                                    entry.priority)) {
-        bool match = !regex_ ||
-                     std::regex_search(entry.message, entry.message + entry.messageLen, *regex_);
+    if (android_log_shouldPrintLine(
+            context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
+            entry.priority)) {
+        bool match = regexOk(context, entry);
 
-        print_count_ += match;
-        if (match || print_it_anyways_) {
-            bytesWritten = android_log_printLogLine(logformat_.get(), output_fd_.get(), &entry);
+        context->printCount += match;
+        if (match || context->printItAnyways) {
+            bytesWritten = android_log_printLogLine(context->logformat,
+                                                    context->output_fd, &entry);
 
             if (bytesWritten < 0) {
-                LogcatPanic(HELP_FALSE, "output error");
+                logcat_panic(context, HELP_FALSE, "output error");
+                return;
             }
         }
     }
 
-    out_byte_count_ += bytesWritten;
+    context->outByteCount += bytesWritten;
 
-    if (log_rotate_size_kb_ > 0 && (out_byte_count_ / 1024) >= log_rotate_size_kb_) {
-        RotateLogs();
+    if (context->logRotateSizeKBytes > 0 &&
+        (context->outByteCount / 1024) >= context->logRotateSizeKBytes) {
+        rotateLogs(context);
     }
 }
 
-void Logcat::PrintDividers(log_id_t log_id, bool print_dividers) {
-    if (log_id == last_printed_id_ || print_binary_) {
-        return;
-    }
-    if (!printed_start_[log_id] || print_dividers) {
-        if (dprintf(output_fd_.get(), "--------- %s %s\n",
-                    printed_start_[log_id] ? "switch to" : "beginning of",
-                    android_log_id_to_name(log_id)) < 0) {
-            LogcatPanic(HELP_FALSE, "output error");
+static void maybePrintStart(android_logcat_context_internal* context,
+                            log_device_t* dev, bool printDividers) {
+    if (!dev->printed || printDividers) {
+        if (context->devCount > 1 && !context->printBinary) {
+            char buf[1024];
+            snprintf(buf, sizeof(buf), "--------- %s %s\n",
+                     dev->printed ? "switch to" : "beginning of", dev->device);
+            if (write(context->output_fd, buf, strlen(buf)) < 0) {
+                logcat_panic(context, HELP_FALSE, "output error");
+                return;
+            }
         }
+        dev->printed = true;
     }
-    last_printed_id_ = log_id;
-    printed_start_[log_id] = true;
 }
 
-void Logcat::SetupOutputAndSchedulingPolicy(bool blocking) {
-    if (!output_file_name_) return;
+static void setupOutputAndSchedulingPolicy(
+    android_logcat_context_internal* context, bool blocking) {
+    if (!context->outputFileName) return;
 
     if (blocking) {
         // Lower priority and set to batch scheduling if we are saving
         // the logs into files and taking continuous content.
-        if (set_sched_policy(0, SP_BACKGROUND) < 0) {
-            fprintf(stderr, "failed to set background scheduling policy\n");
+        if ((set_sched_policy(0, SP_BACKGROUND) < 0) && context->error) {
+            fprintf(context->error,
+                    "failed to set background scheduling policy\n");
         }
 
-        struct sched_param param = {};
+        struct sched_param param;
+        memset(&param, 0, sizeof(param));
         if (sched_setscheduler((pid_t)0, SCHED_BATCH, &param) < 0) {
             fprintf(stderr, "failed to set to batch scheduler\n");
         }
 
-        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
-            fprintf(stderr, "failed set to priority\n");
+        if ((setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) &&
+            context->error) {
+            fprintf(context->error, "failed set to priority\n");
         }
     }
 
-    output_fd_.reset(openLogFile(output_file_name_, log_rotate_size_kb_));
+    close_output(context);
 
-    if (!output_fd_.ok()) {
-        LogcatPanic(HELP_FALSE, "couldn't open output file");
+    context->output_fd = openLogFile(context->outputFileName);
+
+    if (context->output_fd < 0) {
+        logcat_panic(context, HELP_FALSE, "couldn't open output file");
+        return;
     }
 
     struct stat statbuf;
-    if (fstat(output_fd_.get(), &statbuf) == -1) {
-        output_fd_.reset();
-        LogcatPanic(HELP_FALSE, "couldn't get output file stat\n");
+    if (fstat(context->output_fd, &statbuf) == -1) {
+        close_output(context);
+        logcat_panic(context, HELP_FALSE, "couldn't get output file stat\n");
+        return;
     }
 
     if ((size_t)statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
-        output_fd_.reset();
-        LogcatPanic(HELP_FALSE, "invalid output file stat\n");
+        close_output(context);
+        logcat_panic(context, HELP_FALSE, "invalid output file stat\n");
+        return;
     }
 
-    out_byte_count_ = statbuf.st_size;
+    context->output = fdopen(context->output_fd, "web");
+
+    context->outByteCount = statbuf.st_size;
 }
 
 // clang-format off
-static void show_help() {
-    const char* cmd = getprogname();
+static void show_help(android_logcat_context_internal* context) {
+    if (!context->error) return;
 
-    fprintf(stderr, "Usage: %s [options] [filterspecs]\n", cmd);
+    const char* cmd = strrchr(context->argv[0], '/');
+    cmd = cmd ? cmd + 1 : context->argv[0];
 
-    fprintf(stderr, "options include:\n"
+    fprintf(context->error, "Usage: %s [options] [filterspecs]\n", cmd);
+
+    fprintf(context->error, "options include:\n"
                     "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
                     "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
                     "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
@@ -307,7 +460,7 @@
                     "  -d              Dump the log and then exit (don't block)\n"
                     "  -e <expr>, --regex=<expr>\n"
                     "                  Only print lines where the log message matches <expr>\n"
-                    "                  where <expr> is a regular expression\n"
+                    "                  where <expr> is a Perl-compatible regular expression\n"
                     // Leave --head undocumented as alias for -m
                     "  -m <count>, --max-count=<count>\n"
                     "                  Quit after printing <count> lines. This is meant to be\n"
@@ -330,8 +483,7 @@
                     "                  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.\n"
-                    "                  Default -b main,system,crash,kernel.\n"
+                    "                  allowed. Buffers interleaved. Default -b main,system,crash.\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"
@@ -349,7 +501,7 @@
                     "                  comes first. Improves efficiency of polling by providing\n"
                     "                  an about-to-wrap wakeup.\n");
 
-    fprintf(stderr, "\nfilterspecs are a series of \n"
+    fprintf(context->error, "\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
                    "where <tag> is a log component tag (or * for all) and priority is:\n"
                    "  V    Verbose (default for <tag>)\n"
@@ -367,8 +519,9 @@
                    "or defaults to \"threadtime\"\n\n");
 }
 
-static void show_format_help() {
-    fprintf(stderr,
+static void show_format_help(android_logcat_context_internal* context) {
+    if (!context->error) return;
+    fprintf(context->error,
         "-v <format>, --format=<format> options:\n"
         "  Sets log print format verb and adverbs, where <format> is:\n"
         "    brief long process raw tag thread threadtime time\n"
@@ -402,13 +555,16 @@
 }
 // clang-format on
 
-int Logcat::SetLogFormat(const char* format_string) {
-    AndroidLogPrintFormat format = android_log_formatFromString(format_string);
+static int setLogFormat(android_logcat_context_internal* context,
+                        const char* formatString) {
+    AndroidLogPrintFormat format;
+
+    format = android_log_formatFromString(formatString);
 
     // invalid string?
     if (format == FORMAT_OFF) return -1;
 
-    return android_log_setPrintFormat(logformat_.get(), format);
+    return android_log_setPrintFormat(context->logformat, format);
 }
 
 static std::pair<unsigned long, const char*> format_of_size(unsigned long value) {
@@ -421,25 +577,49 @@
     return std::make_pair(value, multipliers[i]);
 }
 
-static void LogcatPanic(enum helpType showHelp, const char* fmt, ...) {
+// String to unsigned int, returns -1 if it fails
+static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
+                        size_t max = SIZE_MAX) {
+    if (!ptr) return false;
+
+    char* endp;
+    errno = 0;
+    size_t ret = (size_t)strtoll(ptr, &endp, 0);
+
+    if (endp[0] || errno) return false;
+
+    if ((ret > max) || (ret < min)) return false;
+
+    *val = ret;
+    return true;
+}
+
+static void logcat_panic(android_logcat_context_internal* context,
+                         enum helpType showHelp, const char* fmt, ...) {
+    context->retval = EXIT_FAILURE;
+    if (!context->error) {
+        context->stop = true;
+        return;
+    }
+
     va_list args;
     va_start(args, fmt);
-    vfprintf(stderr, fmt, args);
+    vfprintf(context->error, fmt, args);
     va_end(args);
 
     switch (showHelp) {
         case HELP_TRUE:
-            show_help();
+            show_help(context);
             break;
         case HELP_FORMAT:
-            show_format_help();
+            show_format_help(context);
             break;
         case HELP_FALSE:
         default:
             break;
     }
 
-    exit(EXIT_FAILURE);
+    context->stop = true;
 }
 
 static char* parseTime(log_time& t, const char* cp) {
@@ -519,18 +699,32 @@
     return retval;
 }
 
-void ReportErrorName(const std::string& name, bool allow_security,
-                     std::vector<std::string>* errors) {
-    if (allow_security || name != "security") {
-        errors->emplace_back(name);
+const char* getenv(android_logcat_context_internal* context, const char* name) {
+    if (!context->envp || !name || !*name) return nullptr;
+
+    for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
+        if (strncmp(context->envp[i], name, len)) continue;
+        if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
+    }
+    return nullptr;
+}
+
+}  // namespace android
+
+void reportErrorName(const char** current, const char* name,
+                     bool blockSecurity) {
+    if (*current) return;
+    if (!blockSecurity || (android_name_to_log_id(name) != LOG_ID_SECURITY)) {
+        *current = name;
     }
 }
 
-int Logcat::Run(int argc, char** argv) {
+static int __logcat(android_logcat_context_internal* context) {
+    using namespace android;
+    int err;
     bool hasSetLogFormat = false;
     bool clearLog = false;
-    bool security_buffer_selected =
-            false;  // Do not report errors on the security buffer unless it is explicitly named.
+    bool allSelected = false;
     bool getLogSize = false;
     bool getPruneList = false;
     bool printStatistics = false;
@@ -540,15 +734,114 @@
     const char* setId = nullptr;
     int mode = ANDROID_LOG_RDONLY;
     std::string forceFilters;
+    log_device_t* dev;
+    struct logger_list* logger_list;
     size_t tail_lines = 0;
     log_time tail_time(log_time::EPOCH);
     size_t pid = 0;
     bool got_t = false;
-    unsigned id_mask = 0;
+
+    // object instantiations before goto's can happen
+    log_device_t unexpected("unexpected", false);
+    const char* openDeviceFail = nullptr;
+    const char* clearFail = nullptr;
+    const char* setSizeFail = nullptr;
+    const char* getSizeFail = nullptr;
+    int argc = context->argc;
+    char* const* argv = context->argv;
+
+    context->output = stdout;
+    context->error = stderr;
+
+    for (int i = 0; i < argc; ++i) {
+        // Simulate shell stderr redirect parsing
+        if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
+
+        // Append to file not implemented, just open file
+        size_t skip = (argv[i][2] == '>') + 2;
+        if (!strcmp(&argv[i][skip], "/dev/null")) {
+            context->stderr_null = true;
+        } else if (!strcmp(&argv[i][skip], "&1")) {
+            context->stderr_stdout = true;
+        } else {
+            // stderr file redirections are not supported
+            fprintf(context->stderr_stdout ? stdout : stderr,
+                    "stderr redirection to file %s unsupported, skipping\n",
+                    &argv[i][skip]);
+        }
+        // Only the first one
+        break;
+    }
+
+    const char* filename = nullptr;
+    for (int i = 0; i < argc; ++i) {
+        // Simulate shell stdout redirect parsing
+        if (argv[i][0] != '>') continue;
+
+        // Append to file not implemented, just open file
+        filename = &argv[i][(argv[i][1] == '>') + 1];
+        // Only the first one
+        break;
+    }
+
+    // Deal with setting up file descriptors and FILE pointers
+    if (context->error_fd >= 0) {  // Is an error file descriptor supplied?
+        if (context->error_fd == context->output_fd) {
+            context->stderr_stdout = true;
+        } else if (context->stderr_null) {  // redirection told us to close it
+            close(context->error_fd);
+            context->error_fd = -1;
+        } else {  // All Ok, convert error to a FILE pointer
+            context->error = fdopen(context->error_fd, "web");
+            if (!context->error) {
+                context->retval = -errno;
+                fprintf(context->stderr_stdout ? stdout : stderr,
+                        "Failed to fdopen(error_fd=%d) %s\n", context->error_fd,
+                        strerror(errno));
+                goto exit;
+            }
+        }
+    }
+    if (context->output_fd >= 0) {  // Is an output file descriptor supplied?
+        if (filename) {  // redirect to file, close supplied file descriptor.
+            close(context->output_fd);
+            context->output_fd = -1;
+        } else {  // All Ok, convert output to a FILE pointer
+            context->output = fdopen(context->output_fd, "web");
+            if (!context->output) {
+                context->retval = -errno;
+                fprintf(context->stderr_stdout ? stdout : context->error,
+                        "Failed to fdopen(output_fd=%d) %s\n",
+                        context->output_fd, strerror(errno));
+                goto exit;
+            }
+        }
+    }
+    if (filename) {  // We supplied an output file redirected in command line
+        context->output = fopen(filename, "web");
+    }
+    // Deal with 2>&1
+    if (context->stderr_stdout) context->error = context->output;
+    // Deal with 2>/dev/null
+    if (context->stderr_null) {
+        context->error_fd = -1;
+        context->error = nullptr;
+    }
+    // Only happens if output=stdout or output=filename
+    if ((context->output_fd < 0) && context->output) {
+        context->output_fd = fileno(context->output);
+    }
+    // Only happens if error=stdout || error=stderr
+    if ((context->error_fd < 0) && context->error) {
+        context->error_fd = fileno(context->error);
+    }
+
+    context->logformat = android_log_format_new();
 
     if (argc == 2 && !strcmp(argv[1], "--help")) {
-        show_help();
-        return EXIT_SUCCESS;
+        show_help(context);
+        context->retval = EXIT_SUCCESS;
+        goto exit;
     }
 
     // meant to catch comma-delimited values, but cast a wider
@@ -605,14 +898,11 @@
             case 0:
                 // only long options
                 if (long_options[option_index].name == pid_str) {
-                    if (pid != 0) {
-                        LogcatPanic(HELP_TRUE, "Only supports one PID argument.\n");
-                    }
-
                     // ToDo: determine runtime PID_MAX?
-                    if (!ParseUint(optarg, &pid) || pid < 1) {
-                        LogcatPanic(HELP_TRUE, "%s %s out of range\n",
-                                    long_options[option_index].name, optarg);
+                    if (!getSizeTArg(optarg, &pid, 1)) {
+                        logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
+                                     long_options[option_index].name, optarg);
+                        goto exit;
                     }
                     break;
                 }
@@ -621,23 +911,26 @@
                             ANDROID_LOG_NONBLOCK;
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
-                    if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
-                        LogcatPanic(HELP_TRUE, "%s %s out of range\n",
-                                    long_options[option_index].name, optarg);
+                    if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
+                        logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
+                                     long_options[option_index].name, optarg);
+                        goto exit;
                     }
-                    if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
-                        fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
-                                long_options[option_index].name, ANDROID_LOG_WRAP_DEFAULT_TIMEOUT,
-                                dummy);
+                    if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
+                        context->error) {
+                        fprintf(context->error,
+                                "WARNING: %s %u seconds, ignoring %zu\n",
+                                long_options[option_index].name,
+                                ANDROID_LOG_WRAP_DEFAULT_TIMEOUT, dummy);
                     }
                     break;
                 }
                 if (long_options[option_index].name == print_str) {
-                    print_it_anyways_ = true;
+                    context->printItAnyways = true;
                     break;
                 }
                 if (long_options[option_index].name == debug_str) {
-                    debug_ = true;
+                    context->debug = true;
                     break;
                 }
                 if (long_options[option_index].name == id_str) {
@@ -647,7 +940,7 @@
 
             case 's':
                 // default to all silent
-                android_log_addFilterRule(logformat_.get(), "*:s");
+                android_log_addFilterRule(context->logformat, "*:s");
                 break;
 
             case 'c':
@@ -672,18 +965,25 @@
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
                     char* cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        LogcatPanic(HELP_FALSE, "-%c \"%s\" not in time format\n", c, optarg);
+                        logcat_panic(context, HELP_FALSE, "-%c \"%s\" not in time format\n", c,
+                                     optarg);
+                        goto exit;
                     }
                     if (*cp) {
                         char ch = *cp;
                         *cp = '\0';
-                        fprintf(stderr, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n", c, optarg,
-                                ch, cp + 1);
+                        if (context->error) {
+                            fprintf(context->error, "WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
+                                    c, optarg, ch, cp + 1);
+                        }
                         *cp = ch;
                     }
                 } else {
-                    if (!ParseUint(optarg, &tail_lines) || tail_lines < 1) {
-                        fprintf(stderr, "WARNING: -%c %s invalid, setting to 1\n", c, optarg);
+                    if (!getSizeTArg(optarg, &tail_lines, 1)) {
+                        if (context->error) {
+                            fprintf(context->error, "WARNING: -%c %s invalid, setting to 1\n", c,
+                                    optarg);
+                        }
                         tail_lines = 1;
                     }
                 }
@@ -694,13 +994,14 @@
                 break;
 
             case 'e':
-                regex_.reset(new std::regex(optarg));
+                context->regex = new pcrecpp::RE(optarg);
                 break;
 
             case 'm': {
-                if (!ParseUint(optarg, &max_count_) || max_count_ < 1) {
-                    LogcatPanic(HELP_FALSE, "-%c \"%s\" isn't an integer greater than zero\n", c,
-                                optarg);
+                if (!getSizeTArg(optarg, &context->maxCount)) {
+                    logcat_panic(context, HELP_FALSE,
+                                 "-%c \"%s\" isn't an integer greater than zero\n", c, optarg);
+                    goto exit;
                 }
             } break;
 
@@ -712,8 +1013,37 @@
                 FALLTHROUGH_INTENDED;
 
             case 'G': {
-                if (!ParseByteCount(optarg, &setLogSize) || setLogSize < 1) {
-                    LogcatPanic(HELP_FALSE, "ERROR: -G <num><multiplier>\n");
+                char* cp;
+                if (strtoll(optarg, &cp, 0) > 0) {
+                    setLogSize = strtoll(optarg, &cp, 0);
+                } else {
+                    setLogSize = 0;
+                }
+
+                switch (*cp) {
+                    case 'g':
+                    case 'G':
+                        setLogSize *= 1024;
+                        FALLTHROUGH_INTENDED;
+                    case 'm':
+                    case 'M':
+                        setLogSize *= 1024;
+                        FALLTHROUGH_INTENDED;
+                    case 'k':
+                    case 'K':
+                        setLogSize *= 1024;
+                        FALLTHROUGH_INTENDED;
+                    case '\0':
+                        break;
+
+                    default:
+                        setLogSize = 0;
+                }
+
+                if (!setLogSize) {
+                    logcat_panic(context, HELP_FALSE,
+                                 "ERROR: -G <num><multiplier>\n");
+                    goto exit;
                 }
             } break;
 
@@ -728,27 +1058,67 @@
                 setPruneList = optarg;
                 break;
 
-            case 'b':
-                for (const auto& buffer : Split(optarg, delimiters)) {
-                    if (buffer == "default") {
-                        id_mask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH);
-                    } else if (buffer == "all") {
-                        id_mask = -1;
+            case 'b': {
+                std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free);
+                char* arg = buffers.get();
+                unsigned idMask = 0;
+                char* sv = nullptr;  // protect against -ENOMEM above
+                while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+                    if (!strcmp(arg, "default")) {
+                        idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
+                                  (1 << LOG_ID_CRASH);
+                    } else if (!strcmp(arg, "all")) {
+                        allSelected = true;
+                        idMask = (unsigned)-1;
                     } else {
-                        log_id_t log_id = android_name_to_log_id(buffer.c_str());
-                        if (log_id >= LOG_ID_MAX) {
-                            LogcatPanic(HELP_TRUE, "unknown buffer %s\n", buffer.c_str());
+                        log_id_t log_id = android_name_to_log_id(arg);
+                        const char* name = android_log_id_to_name(log_id);
+
+                        if (!!strcmp(name, arg)) {
+                            logcat_panic(context, HELP_TRUE,
+                                         "unknown buffer %s\n", arg);
+                            goto exit;
                         }
-                        if (log_id == LOG_ID_SECURITY) {
-                            security_buffer_selected = true;
-                        }
-                        id_mask |= (1 << log_id);
+                        if (log_id == LOG_ID_SECURITY) allSelected = false;
+                        idMask |= (1 << log_id);
                     }
+                    arg = nullptr;
                 }
-                break;
+
+                for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
+                    const char* name = android_log_id_to_name((log_id_t)i);
+                    log_id_t log_id = android_name_to_log_id(name);
+
+                    if (log_id != (log_id_t)i) continue;
+                    if (!(idMask & (1 << i))) continue;
+
+                    bool found = false;
+                    for (dev = context->devices; dev; dev = dev->next) {
+                        if (!strcmp(name, dev->device)) {
+                            found = true;
+                            break;
+                        }
+                        if (!dev->next) break;
+                    }
+                    if (found) continue;
+
+                    bool binary = !strcmp(name, "events") ||
+                                  !strcmp(name, "security") ||
+                                  !strcmp(name, "stats");
+                    log_device_t* d = new log_device_t(name, binary);
+
+                    if (dev) {
+                        dev->next = d;
+                        dev = d;
+                    } else {
+                        context->devices = dev = d;
+                    }
+                    context->devCount++;
+                }
+            } break;
 
             case 'B':
-                print_binary_ = 1;
+                context->printBinary = 1;
                 break;
 
             case 'f':
@@ -756,34 +1126,43 @@
                     tail_time = lastLogTime(optarg);
                 }
                 // redirect output to a file
-                output_file_name_ = optarg;
+                context->outputFileName = optarg;
                 break;
 
             case 'r':
-                if (!ParseUint(optarg, &log_rotate_size_kb_) || log_rotate_size_kb_ < 1) {
-                    LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -r\n", 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 (!ParseUint(optarg, &max_rotated_logs_) || max_rotated_logs_ < 1) {
-                    LogcatPanic(HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
+                if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+                    logcat_panic(context, HELP_TRUE, "Invalid parameter \"%s\" to -n\n", optarg);
+                    goto exit;
                 }
                 break;
 
-            case 'v':
+            case 'v': {
                 if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
-                    show_format_help();
-                    return EXIT_SUCCESS;
+                    show_format_help(context);
+                    context->retval = EXIT_SUCCESS;
+                    goto exit;
                 }
-                for (const auto& arg : Split(optarg, delimiters)) {
-                    int err = SetLogFormat(arg.c_str());
+                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))) {
+                    err = setLogFormat(context, arg);
                     if (err < 0) {
-                        LogcatPanic(HELP_FORMAT, "Invalid parameter \"%s\" to -v\n", arg.c_str());
+                        logcat_panic(context, HELP_FORMAT,
+                                     "Invalid parameter \"%s\" to -v\n", arg);
+                        goto exit;
                     }
+                    arg = nullptr;
                     if (err) hasSetLogFormat = true;
                 }
-                break;
+            } break;
 
             case 'Q':
 #define LOGCAT_FILTER "androidboot.logcat="
@@ -801,7 +1180,8 @@
                 {
                     // if not in emulator, exit quietly
                     if (false == android::base::GetBoolProperty(QEMU_PROPERTY, false)) {
-                        return EXIT_SUCCESS;
+                        context->retval = EXIT_SUCCESS;
+                        goto exit;
                     }
 
                     std::string cmdline = android::base::GetProperty(QEMU_CMDLINE, "");
@@ -812,7 +1192,8 @@
                     const char* logcatFilter = strstr(cmdline.c_str(), LOGCAT_FILTER);
                     // if nothing found or invalid filters, exit quietly
                     if (!logcatFilter) {
-                        return EXIT_SUCCESS;
+                        context->retval = EXIT_SUCCESS;
+                        goto exit;
                     }
 
                     const char* p = logcatFilter + strlen(LOGCAT_FILTER);
@@ -831,7 +1212,8 @@
                     } else if (console) {
                         p = console + strlen(CONSOLE_OPTION);
                     } else {
-                        return EXIT_FAILURE;
+                        context->retval = EXIT_FAILURE;
+                        goto exit;
                     }
 
                     q = strpbrk(p, " \t\n\r");
@@ -848,26 +1230,37 @@
                             devname = devname.substr(0, pos);
                         }
                     }
+                    cmdline.erase();
 
-                    fprintf(stderr, "logcat using %s\n", devname.c_str());
-
-                    int fd = open(devname.c_str(), O_WRONLY | O_CLOEXEC);
-                    if (fd < 0) {
-                        break;
+                    if (context->error) {
+                        fprintf(context->error, "logcat using %s\n",
+                                devname.c_str());
                     }
 
+                    FILE* fp = fopen(devname.c_str(), "web");
+                    devname.erase();
+                    if (!fp) break;
+
                     if (consolePipe) {
                         // need the trailing '\0'
-                        if (!android::base::WriteFully(fd, pipePurpose.c_str(),
-                                                       pipePurpose.size() + 1)) {
-                            close(fd);
-                            return EXIT_FAILURE;
+                        if(!android::base::WriteFully(fileno(fp), pipePurpose.c_str(),
+                                    pipePurpose.size() + 1)) {
+                            fclose(fp);
+                            context->retval = EXIT_FAILURE;
+                            goto exit;
                         }
                     }
+
                     // close output and error channels, replace with console
-                    dup2(fd, STDOUT_FILENO);
-                    dup2(fd, STDERR_FILENO);
-                    close(fd);
+                    android::close_output(context);
+                    android::close_error(context);
+                    context->stderr_stdout = true;
+                    context->output = fp;
+                    context->output_fd = fileno(fp);
+                    if (context->stderr_null) break;
+                    context->stderr_stdout = true;
+                    context->error = fp;
+                    context->error_fd = fileno(fp);
                 }
                 break;
 
@@ -876,48 +1269,65 @@
                 break;
 
             case ':':
-                LogcatPanic(HELP_TRUE, "Option -%c needs an argument\n", optopt);
+                logcat_panic(context, HELP_TRUE, "Option -%c needs an argument\n", optopt);
+                goto exit;
 
             case 'h':
-                show_help();
-                show_format_help();
-                return EXIT_SUCCESS;
+                show_help(context);
+                show_format_help(context);
+                goto exit;
 
             default:
-                LogcatPanic(HELP_TRUE, "Unrecognized Option %c\n", optopt);
+                logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt);
+                goto exit;
         }
     }
 
-    if (max_count_ && got_t) {
-        LogcatPanic(HELP_TRUE, "Cannot use -m (--max-count) and -t together\n");
+    if (context->maxCount && got_t) {
+        logcat_panic(context, HELP_TRUE,
+                     "Cannot use -m (--max-count) and -t together\n");
+        goto exit;
     }
-    if (print_it_anyways_ && (!regex_ || !max_count_)) {
+    if (context->printItAnyways && (!context->regex || !context->maxCount)) {
         // One day it would be nice if --print -v color and --regex <expr>
         // could play with each other and show regex highlighted content.
-        fprintf(stderr,
-                "WARNING: "
-                "--print ignored, to be used in combination with\n"
-                "         "
-                "--regex <expr> and --max-count <N>\n");
-        print_it_anyways_ = false;
+        // clang-format off
+        if (context->error) {
+            fprintf(context->error, "WARNING: "
+                            "--print ignored, to be used in combination with\n"
+                                "         "
+                            "--regex <expr> and --max-count <N>\n");
+        }
+        context->printItAnyways = false;
     }
 
-    // If no buffers are specified, default to using these buffers.
-    if (id_mask == 0) {
-        id_mask = (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH) |
-                  (1 << LOG_ID_KERNEL);
+    if (!context->devices) {
+        dev = context->devices = new log_device_t("main", false);
+        context->devCount = 1;
+        if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
+            dev = dev->next = new log_device_t("system", false);
+            context->devCount++;
+        }
+        if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
+            dev = dev->next = new log_device_t("crash", false);
+            context->devCount++;
+        }
     }
 
-    if (log_rotate_size_kb_ != 0 && !output_file_name_) {
-        LogcatPanic(HELP_TRUE, "-r requires -f as well\n");
+    if (!!context->logRotateSizeKBytes && !context->outputFileName) {
+        logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
+        goto exit;
     }
 
-    if (setId != 0) {
-        if (!output_file_name_) {
-            LogcatPanic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
+    if (!!setId) {
+        if (!context->outputFileName) {
+            logcat_panic(context, HELP_TRUE,
+                         "--id='%s' requires -f as well\n", setId);
+            goto exit;
         }
 
-        std::string file_name = StringPrintf("%s.id", output_file_name_);
+        std::string file_name = android::base::StringPrintf(
+                                        "%s.id", context->outputFileName);
         std::string file;
         bool file_ok = android::base::ReadFileToString(file_name, &file);
         android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
@@ -926,149 +1336,175 @@
     }
 
     if (!hasSetLogFormat) {
-        const char* logFormat = getenv("ANDROID_PRINTF_LOG");
+        const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
 
         if (!!logFormat) {
-            for (const auto& arg : Split(logFormat, delimiters)) {
-                int err = SetLogFormat(arg.c_str());
+            std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
+                                                           free);
+            char* sv = nullptr;  // protect against -ENOMEM above
+            char* arg = formats.get();
+            while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+                err = setLogFormat(context, arg);
                 // environment should not cause crash of logcat
-                if (err < 0) {
-                    fprintf(stderr, "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg.c_str());
+                if ((err < 0) && context->error) {
+                    fprintf(context->error,
+                            "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
                 }
+                arg = nullptr;
                 if (err > 0) hasSetLogFormat = true;
             }
         }
         if (!hasSetLogFormat) {
-            SetLogFormat("threadtime");
+            setLogFormat(context, "threadtime");
         }
     }
 
     if (forceFilters.size()) {
-        int err = android_log_addFilterString(logformat_.get(), forceFilters.c_str());
+        err = android_log_addFilterString(context->logformat,
+                                          forceFilters.c_str());
         if (err < 0) {
-            LogcatPanic(HELP_FALSE, "Invalid filter expression in logcat args\n");
+            logcat_panic(context, HELP_FALSE,
+                         "Invalid filter expression in logcat args\n");
+            goto exit;
         }
     } else if (argc == optind) {
         // Add from environment variable
-        const char* env_tags_orig = getenv("ANDROID_LOG_TAGS");
+        const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
 
         if (!!env_tags_orig) {
-            int err = android_log_addFilterString(logformat_.get(), env_tags_orig);
+            err = android_log_addFilterString(context->logformat,
+                                              env_tags_orig);
 
             if (err < 0) {
-                LogcatPanic(HELP_TRUE, "Invalid filter expression in ANDROID_LOG_TAGS\n");
+                logcat_panic(context, HELP_TRUE,
+                            "Invalid filter expression in ANDROID_LOG_TAGS\n");
+                goto exit;
             }
         }
     } else {
         // Add from commandline
         for (int i = optind ; i < argc ; i++) {
-            int err = android_log_addFilterString(logformat_.get(), argv[i]);
+            // skip stderr redirections of _all_ kinds
+            if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
+            // skip stdout redirections of _all_ kinds
+            if (argv[i][0] == '>') continue;
+
+            err = android_log_addFilterString(context->logformat, argv[i]);
             if (err < 0) {
-                LogcatPanic(HELP_TRUE, "Invalid filter expression '%s'\n", argv[i]);
+                logcat_panic(context, HELP_TRUE,
+                             "Invalid filter expression '%s'\n", argv[i]);
+                goto exit;
             }
         }
     }
 
-    std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
-            nullptr, &android_logger_list_free};
+    dev = context->devices;
     if (tail_time != log_time::EPOCH) {
-        logger_list.reset(android_logger_list_alloc_time(mode, tail_time, pid));
+        logger_list = android_logger_list_alloc_time(mode, tail_time, pid);
     } else {
-        logger_list.reset(android_logger_list_alloc(mode, tail_lines, pid));
+        logger_list = android_logger_list_alloc(mode, tail_lines, pid);
     }
     // We have three orthogonal actions below to clear, set log size and
     // get log size. All sharing the same iteration loop.
-    std::vector<std::string> open_device_failures;
-    std::vector<std::string> clear_failures;
-    std::vector<std::string> set_size_failures;
-    std::vector<std::string> get_size_failures;
-
-    for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-        if (!(id_mask & (1 << i))) continue;
-        const char* buffer_name = android_log_id_to_name(static_cast<log_id_t>(i));
-
-        auto logger = android_logger_open(logger_list.get(), static_cast<log_id_t>(i));
-        if (logger == nullptr) {
-            ReportErrorName(buffer_name, security_buffer_selected, &open_device_failures);
+    while (dev) {
+        dev->logger_list = logger_list;
+        dev->logger = android_logger_open(logger_list,
+                                          android_name_to_log_id(dev->device));
+        if (!dev->logger) {
+            reportErrorName(&openDeviceFail, dev->device, allSelected);
+            dev = dev->next;
             continue;
         }
 
         if (clearLog || setId) {
-            if (output_file_name_) {
-                int max_rotation_count_digits =
-                        max_rotated_logs_ > 0 ? (int)(floor(log10(max_rotated_logs_) + 1)) : 0;
+            if (context->outputFileName) {
+                int maxRotationCountDigits =
+                    (context->maxRotatedLogs > 0) ?
+                        (int)(floor(log10(context->maxRotatedLogs) + 1)) :
+                        0;
 
-                for (int i = max_rotated_logs_; i >= 0; --i) {
+                for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
                     std::string file;
 
                     if (!i) {
-                        file = output_file_name_;
+                        file = android::base::StringPrintf(
+                            "%s", context->outputFileName);
                     } else {
-                        file = StringPrintf("%s.%.*d", output_file_name_, max_rotation_count_digits,
-                                            i);
+                        file = android::base::StringPrintf("%s.%.*d",
+                            context->outputFileName, maxRotationCountDigits, i);
                     }
 
                     if (!file.length()) {
                         perror("while clearing log files");
-                        ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
+                        reportErrorName(&clearFail, dev->device, allSelected);
                         break;
                     }
 
-                    int err = unlink(file.c_str());
+                    err = unlink(file.c_str());
 
-                    if (err < 0 && errno != ENOENT) {
+                    if (err < 0 && errno != ENOENT && !clearFail) {
                         perror("while clearing log files");
-                        ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
+                        reportErrorName(&clearFail, dev->device, allSelected);
                     }
                 }
-            } else if (android_logger_clear(logger)) {
-                ReportErrorName(buffer_name, security_buffer_selected, &clear_failures);
+            } else if (android_logger_clear(dev->logger)) {
+                reportErrorName(&clearFail, dev->device, allSelected);
             }
         }
 
         if (setLogSize) {
-            if (android_logger_set_log_size(logger, setLogSize)) {
-                ReportErrorName(buffer_name, security_buffer_selected, &set_size_failures);
+            if (android_logger_set_log_size(dev->logger, setLogSize)) {
+                reportErrorName(&setSizeFail, dev->device, allSelected);
             }
         }
 
         if (getLogSize) {
-            long size = android_logger_get_log_size(logger);
-            long readable = android_logger_get_log_readable_size(logger);
+            long size = android_logger_get_log_size(dev->logger);
+            long readable = android_logger_get_log_readable_size(dev->logger);
 
-            if (size < 0 || readable < 0) {
-                ReportErrorName(buffer_name, security_buffer_selected, &get_size_failures);
+            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 %lu %sB (%lu %sB consumed),"
-                        " max entry is %d B, max payload is %d B\n",
-                        buffer_name, 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(output_fd_.get(), str.data(), str.length()));
+                       "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+                         " max entry is %d B, max payload is %d B\n",
+                       dev->device,
+                       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,
+                                         str.data(), str.length()));
             }
         }
+
+        dev = dev->next;
     }
 
+    context->retval = EXIT_SUCCESS;
+
     // report any errors in the above loop and exit
-    if (!open_device_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "Unable to open log device%s '%s'\n",
-                    open_device_failures.size() > 1 ? "s" : "",
-                    Join(open_device_failures, ",").c_str());
+    if (openDeviceFail) {
+        logcat_panic(context, HELP_FALSE,
+                     "Unable to open log device '%s'\n", openDeviceFail);
+        goto close;
     }
-    if (!clear_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to clear the '%s' log%s\n",
-                    Join(clear_failures, ",").c_str(), clear_failures.size() > 1 ? "s" : "");
+    if (clearFail) {
+        logcat_panic(context, HELP_FALSE,
+                     "failed to clear the '%s' log\n", clearFail);
+        goto close;
     }
-    if (!set_size_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to set the '%s' log size%s\n",
-                    Join(set_size_failures, ",").c_str(), set_size_failures.size() > 1 ? "s" : "");
+    if (setSizeFail) {
+        logcat_panic(context, HELP_FALSE,
+                     "failed to set the '%s' log size\n", setSizeFail);
+        goto close;
     }
-    if (!get_size_failures.empty()) {
-        LogcatPanic(HELP_FALSE, "failed to get the readable '%s' log size%s\n",
-                    Join(get_size_failures, ",").c_str(), get_size_failures.size() > 1 ? "s" : "");
+    if (getSizeFail) {
+        logcat_panic(context, HELP_FALSE,
+                     "failed to get the readable '%s' log size", getSizeFail);
+        goto close;
     }
 
     if (setPruneList) {
@@ -1078,14 +1514,16 @@
         char* buf = nullptr;
         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
             buf[len] = '\0';
-            if (android_logger_set_prune_list(logger_list.get(), buf, bLen)) {
-                LogcatPanic(HELP_FALSE, "failed to set the prune list");
+            if (android_logger_set_prune_list(logger_list, buf, bLen)) {
+                logcat_panic(context, HELP_FALSE,
+                             "failed to set the prune list");
             }
             free(buf);
         } else {
-            LogcatPanic(HELP_FALSE, "failed to set the prune list (alloc)");
+            logcat_panic(context, HELP_FALSE,
+                         "failed to set the prune list (alloc)");
         }
-        return EXIT_SUCCESS;
+        goto close;
     }
 
     if (printStatistics || getPruneList) {
@@ -1095,9 +1533,9 @@
         for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
              delete[] buf, buf = nullptr, --retry) {
             if (getPruneList) {
-                android_logger_get_prune_list(logger_list.get(), buf, len);
+                android_logger_get_prune_list(logger_list, buf, len);
             } else {
-                android_logger_get_statistics(logger_list.get(), buf, len);
+                android_logger_get_statistics(logger_list, buf, len);
             }
             buf[len - 1] = '\0';
             if (atol(buf) < 3) {
@@ -1114,7 +1552,8 @@
         }
 
         if (!buf) {
-            LogcatPanic(HELP_FALSE, "failed to read data");
+            logcat_panic(context, HELP_FALSE, "failed to read data");
+            goto close;
         }
 
         // remove trailing FF
@@ -1131,51 +1570,164 @@
         }
 
         len = strlen(cp);
-        TEMP_FAILURE_RETRY(write(output_fd_.get(), cp, len));
+        TEMP_FAILURE_RETRY(write(context->output_fd, cp, len));
         delete[] buf;
-        return EXIT_SUCCESS;
+        goto close;
     }
 
-    if (getLogSize || setLogSize || clearLog) return EXIT_SUCCESS;
+    if (getLogSize || setLogSize || clearLog) goto close;
 
-    SetupOutputAndSchedulingPolicy(!(mode & ANDROID_LOG_NONBLOCK));
+    setupOutputAndSchedulingPolicy(context, !(mode & ANDROID_LOG_NONBLOCK));
+    if (context->stop) goto close;
 
-    while (!max_count_ || print_count_ < max_count_) {
+    // LOG_EVENT_INT(10, 12345);
+    // LOG_EVENT_LONG(11, 0x1122334455667788LL);
+    // LOG_EVENT_STRING(0, "whassup, doc?");
+
+    dev = nullptr;
+
+    while (!context->stop &&
+           (!context->maxCount || (context->printCount < context->maxCount))) {
         struct log_msg log_msg;
-        int ret = android_logger_list_read(logger_list.get(), &log_msg);
+        int ret = android_logger_list_read(logger_list, &log_msg);
         if (!ret) {
-            LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
+            logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
+            break;
         }
 
         if (ret < 0) {
             if (ret == -EAGAIN) break;
 
             if (ret == -EIO) {
-                LogcatPanic(HELP_FALSE, "read: unexpected EOF!\n");
+                logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
+                break;
             }
             if (ret == -EINVAL) {
-                LogcatPanic(HELP_FALSE, "read: unexpected length.\n");
+                logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
+                break;
             }
-            LogcatPanic(HELP_FALSE, "logcat read failure\n");
+            logcat_panic(context, HELP_FALSE, "logcat read failure\n");
+            break;
         }
 
-        if (log_msg.id() > LOG_ID_MAX) {
-            LogcatPanic(HELP_FALSE, "read: unexpected log id (%d) over LOG_ID_MAX (%d)",
-                        log_msg.id(), LOG_ID_MAX);
+        log_device_t* d;
+        for (d = context->devices; d; d = d->next) {
+            if (android_name_to_log_id(d->device) == log_msg.id()) break;
+        }
+        if (!d) {
+            context->devCount = 2; // set to Multiple
+            d = &unexpected;
+            d->binary = log_msg.id() == LOG_ID_EVENTS;
         }
 
-        PrintDividers(log_msg.id(), printDividers);
-
-        if (print_binary_) {
-            TEMP_FAILURE_RETRY(write(output_fd_.get(), &log_msg, log_msg.len()));
+        if (dev != d) {
+            dev = d;
+            maybePrintStart(context, dev, printDividers);
+            if (context->stop) break;
+        }
+        if (context->printBinary) {
+            printBinary(context, &log_msg);
         } else {
-            ProcessBuffer(&log_msg);
+            processBuffer(context, dev, &log_msg);
         }
     }
-    return EXIT_SUCCESS;
+
+close:
+    // Short and sweet. Implemented generic version in android_logcat_destroy.
+    while (!!(dev = context->devices)) {
+        context->devices = dev->next;
+        delete dev;
+    }
+    android_logger_list_free(logger_list);
+
+exit:
+    // close write end of pipe to help things along
+    if (context->output_fd == context->fds[1]) {
+        android::close_output(context);
+    }
+    if (context->error_fd == context->fds[1]) {
+        android::close_error(context);
+    }
+    if (context->fds[1] >= 0) {
+        // NB: should be closed by the above
+        int save_errno = errno;
+        close(context->fds[1]);
+        errno = save_errno;
+        context->fds[1] = -1;
+    }
+    context->thread_stopped = true;
+    return context->retval;
 }
 
-int main(int argc, char** argv) {
-    Logcat logcat;
-    return logcat.Run(argc, argv);
+// Can block
+int android_logcat_run_command(android_logcat_context ctx,
+                               int output, int error,
+                               int argc, char* const* argv,
+                               char* const* envp) {
+    android_logcat_context_internal* context = ctx;
+
+    context->output_fd = output;
+    context->error_fd = error;
+    context->argc = argc;
+    context->argv = argv;
+    context->envp = envp;
+    context->stop = false;
+    context->thread_stopped = false;
+    return __logcat(context);
+}
+
+// Finished with context
+int android_logcat_destroy(android_logcat_context* ctx) {
+    android_logcat_context_internal* context = *ctx;
+
+    if (!context) return -EBADF;
+
+    *ctx = nullptr;
+
+    context->stop = true;
+
+    while (context->thread_stopped == false) {
+        // Makes me sad, replace thread_stopped with semaphore.  Short lived.
+        sched_yield();
+    }
+
+    delete context->regex;
+    context->argv_hold.clear();
+    context->args.clear();
+    context->envp_hold.clear();
+    context->envs.clear();
+    if (context->fds[0] >= 0) {
+        close(context->fds[0]);
+        context->fds[0] = -1;
+    }
+    android::close_output(context);
+    android::close_error(context);
+
+    if (context->fds[1] >= 0) {
+        // NB: this should be closed by close_output, but just in case...
+        close(context->fds[1]);
+        context->fds[1] = -1;
+    }
+
+    android_closeEventTagMap(context->eventTagMap);
+
+    // generic cleanup of devices list to handle all possible dirty cases
+    log_device_t* dev;
+    while (!!(dev = context->devices)) {
+        struct logger_list* logger_list = dev->logger_list;
+        if (logger_list) {
+            for (log_device_t* d = dev; d; d = d->next) {
+                if (d->logger_list == logger_list) d->logger_list = nullptr;
+            }
+            android_logger_list_free(logger_list);
+        }
+        context->devices = dev->next;
+        delete dev;
+    }
+
+    int retval = context->retval;
+
+    free(context);
+
+    return retval;
 }
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
new file mode 100644
index 0000000..ecfa2ba
--- /dev/null
+++ b/logcat/logcat_main.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+
+#include "logcat.h"
+
+int main(int argc, char** argv, char** envp) {
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+    signal(SIGPIPE, exit);
+    int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp);
+    int ret = android_logcat_destroy(&ctx);
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/logcatd b/logcat/logcatd
deleted file mode 100755
index 622e567..0000000
--- a/logcat/logcatd
+++ /dev/null
@@ -1,25 +0,0 @@
-#! /system/bin/sh
-
-# This is primarily meant to be used by logpersist.  This script is run as an init service, which
-# first reads the 'last' logcat to persistent storage with `-L` then run logcat again without
-# `-L` to read the current logcat buffers to persistent storage.
-
-has_last="false"
-for arg in "$@"; do
-  if [ "$arg" == "-L" -o "$arg" == "--last" ]; then
-    has_last="true"
-  fi
-done
-
-if [ "$has_last" == "true" ]; then
-  logcat "$@"
-fi
-
-args_without_last=()
-for arg in "$@"; do
-  if [ "$arg" != "-L" -a "$arg" != "--last" ]; then
-    ARGS+=("$arg")
-  fi
-done
-
-exec logcat "${ARGS[@]}"
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index e986184..07040b0 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -4,22 +4,24 @@
 # 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 logd.logpersistd.rotate_kbytes and
-# logd.logpersistd.buffer.
+# notwithstanding) for logd.logpersistd.size 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 ""
 
 on property:persist.logd.logpersistd.size=*
+    # expect /init to report failure if property empty (default)
     setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
 
-on property:persist.logd.logpersistd.rotate_kbytes=*
-    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 ""
 
 on property:persist.logd.logpersistd.buffer=*
+    # expect /init to report failure if property empty (default)
     setprop logd.logpersistd.buffer ${persist.logd.logpersistd.buffer}
 
 on property:persist.logd.logpersistd=logcatd
@@ -52,7 +54,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 ${logd.logpersistd.rotate_kbytes:-2048} -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 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
new file mode 100644
index 0000000..c131846
--- /dev/null
+++ b/logcat/logcatd_main.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "logcat.h"
+
+int main(int argc, char** argv, char** envp) {
+    android_logcat_context ctx = create_android_logcat();
+    if (!ctx) return -1;
+
+    signal(SIGPIPE, exit);
+
+    // Save and detect presence of -L or --last flag
+    std::vector<std::string> args;
+    bool last = false;
+    for (int i = 0; i < argc; ++i) {
+        if (!argv[i]) continue;
+        args.push_back(std::string(argv[i]));
+        if (!strcmp(argv[i], "-L") || !strcmp(argv[i], "--last")) last = true;
+    }
+
+    // Generate argv from saved content
+    std::vector<const char*> argv_hold;
+    for (auto& str : args) argv_hold.push_back(str.c_str());
+    argv_hold.push_back(nullptr);
+
+    int ret = 0;
+    if (last) {
+        // Run logcat command with -L flag
+        ret = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+                                         (char* const*)&argv_hold[0], envp);
+        // Remove -L and --last flags from argument list
+        for (std::vector<const char*>::iterator it = argv_hold.begin();
+             it != argv_hold.end();) {
+            if (!*it || (strcmp(*it, "-L") && strcmp(*it, "--last"))) {
+                ++it;
+            } else {
+                it = argv_hold.erase(it);
+            }
+        }
+        // fall through to re-run the command regardless of the arguments
+        // passed in.  For instance, we expect -h to report help stutter.
+    }
+
+    // Run logcat command without -L flag
+    int retval = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+                                            (char* const*)&argv_hold[0], envp);
+    if (!ret) ret = retval;
+    retval = android_logcat_destroy(&ctx);
+    if (!ret) ret = retval;
+    return ret;
+}
diff --git a/logcat/logpersist b/logcat/logpersist
index 05b46f0..c09b6b2 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -148,9 +148,9 @@
     echo "WARNING: Can not use --size or --buffer with ${progname%.*}.stop" >&2
   fi
   if [ "true" = "${clear}" ]; then
-    setprop ${property#persist.} "clear"
+    setprop ${property} "clear"
   else
-    setprop ${property#persist.} "stop"
+    setprop ${property} "stop"
   fi
   if [ -n "`getprop ${property#persist.}.buffer`" ]; then
     setprop ${property}.buffer ""
diff --git a/logd/Android.bp b/logd/Android.bp
index b337b7c..9b86258 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -39,6 +39,7 @@
         "FlushCommand.cpp",
         "LogBuffer.cpp",
         "LogBufferElement.cpp",
+        "LogBufferInterface.cpp",
         "LogTimes.cpp",
         "LogStatistics.cpp",
         "LogWhiteBlackList.cpp",
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index d9cc0db..a21555c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -143,6 +143,10 @@
 
 void LogAudit::auditParse(const std::string& string, uid_t uid,
                           std::string* bug_num) {
+    if (!__android_log_is_debuggable()) {
+        bug_num->assign("");
+        return;
+    }
     static std::map<std::string, std::string> denial_to_bug =
         populateDenialMap();
     std::string scontext = denialParse(string, ':', "scontext=u:object_r:");
@@ -156,7 +160,7 @@
     }
     auto search = denial_to_bug.find(scontext + tcontext + tclass);
     if (search != denial_to_bug.end()) {
-        bug_num->assign(" " + search->second);
+        bug_num->assign(" b/" + search->second);
     } else {
         bug_num->assign("");
     }
@@ -225,17 +229,70 @@
         static const char log_warning[] = { KMSG_PRIORITY(LOG_WARNING) };
         static const char newline[] = "\n";
 
-        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);
+        // Dedupe messages, checking for identical messages starting with avc:
+        static unsigned count;
+        static char* last_str;
+        static bool last_info;
 
-        writev(fdDmesg, iov, arraysize(iov));
+        if (last_str != nullptr) {
+            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*>(denial_metadata.c_str());
+                iov[2].iov_len = denial_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 = nullptr;
+            }
+        }
+        if (last_str == nullptr) {
+            count = 0;
+            last_str = strdup(str);
+            last_info = info;
+        }
+        if (count == 0) {
+            auditParse(str, uid, &denial_metadata);
+            iov[0].iov_base = info ? const_cast<char*>(log_info)
+                                   : const_cast<char*>(log_warning);
+            iov[0].iov_len = info ? sizeof(log_info) : sizeof(log_warning);
+            iov[1].iov_base = str;
+            iov[1].iov_len = strlen(str);
+            iov[2].iov_base = const_cast<char*>(denial_metadata.c_str());
+            iov[2].iov_len = denial_metadata.length();
+            iov[3].iov_base = const_cast<char*>(newline);
+            iov[3].iov_len = strlen(newline);
+
+            writev(fdDmesg, iov, arraysize(iov));
+        }
     }
 
     if (!main && !events) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index ba05a06..9cbc7c4 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -1185,7 +1185,7 @@
         unlock();
 
         // range locking in LastLogTimes looks after us
-        curr = element->flushTo(reader, this, sameTid);
+        curr = element->flushTo(reader, this, privileged, sameTid);
 
         if (curr == element->FLUSH_ERROR) {
             return curr;
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index c2d5b97..774d4ab 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,6 +27,7 @@
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
+#include "LogBufferInterface.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogTimes.h"
@@ -74,7 +75,7 @@
 
 typedef std::list<LogBufferElement*> LogBufferElementCollection;
 
-class LogBuffer {
+class LogBuffer : public LogBufferInterface {
     LogBufferElementCollection mLogElements;
     pthread_rwlock_t mLogElementsLock;
 
@@ -107,14 +108,14 @@
     LastLogTimes& mTimes;
 
     explicit LogBuffer(LastLogTimes* times);
-    ~LogBuffer();
+    ~LogBuffer() override;
     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,
-            uint16_t len);
+    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) override;
     // 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)
@@ -158,7 +159,12 @@
     const char* pidToName(pid_t pid) {
         return stats.pidToName(pid);
     }
-    uid_t pidToUid(pid_t pid) { return stats.pidToUid(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);
+    }
     const char* uidToName(uid_t uid) {
         return stats.uidToName(uid);
     }
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index ec81933..2fd9f95 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -56,7 +56,14 @@
       mLogId(elem.mLogId),
       mDropped(elem.mDropped) {
     if (mDropped) {
-        mTag = elem.getTag();
+        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);
@@ -64,43 +71,31 @@
 }
 
 LogBufferElement::~LogBufferElement() {
-    if (!mDropped) {
-        delete[] mMsg;
-    }
+    delete[] mMsg;
 }
 
 uint32_t LogBufferElement::getTag() const {
-    // Binary buffers have no tag.
-    if (!isBinary()) {
-        return 0;
-    }
-
-    // Dropped messages store the tag in place of mMsg.
-    if (mDropped) {
-        return mTag;
-    }
-
-    // For non-dropped messages, we get the tag from the message header itself.
-    if (mMsgLen < sizeof(android_event_header_t)) {
-        return 0;
-    }
-
-    return reinterpret_cast<const android_event_header_t*>(mMsg)->tag;
+    return (isBinary() &&
+            ((mDropped && mMsg != nullptr) ||
+             (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
+               ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
+               : 0;
 }
 
 uint16_t LogBufferElement::setDropped(uint16_t value) {
-    if (mDropped) {
-        return mDroppedCount = value;
+    // The tag information is saved in mMsg data, if the tag is non-zero
+    // save only the information needed to get the tag.
+    if (getTag() != 0) {
+        if (mMsgLen > sizeof(android_event_header_t)) {
+            char* truncated_msg = new char[sizeof(android_event_header_t)];
+            memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
+            delete[] mMsg;
+            mMsg = truncated_msg;
+        }  // mMsgLen == sizeof(android_event_header_t), already at minimum.
+    } else {
+        delete[] mMsg;
+        mMsg = nullptr;
     }
-
-    // The tag information is saved in mMsg data, which is in a union with mTag, used after mDropped
-    // is set to true. Therefore we save the tag value aside, delete mMsg, then set mTag to the tag
-    // value in its place.
-    auto old_tag = getTag();
-    delete[] mMsg;
-    mMsg = nullptr;
-
-    mTag = old_tag;
     mDropped = true;
     return mDroppedCount = value;
 }
@@ -244,10 +239,14 @@
     return retval;
 }
 
-log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
-    struct logger_entry entry = {};
+log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent,
+                                   bool privileged, bool lastSame) {
+    struct logger_entry_v4 entry;
 
-    entry.hdr_size = sizeof(struct logger_entry);
+    memset(&entry, 0, sizeof(struct logger_entry_v4));
+
+    entry.hdr_size = privileged ? sizeof(struct logger_entry_v4)
+                                : sizeof(struct logger_entry_v3);
     entry.lid = mLogId;
     entry.pid = mPid;
     entry.tid = mTid;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index fd790e4..57b0a95 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _LOGD_LOG_BUFFER_ELEMENT_H__
+#define _LOGD_LOG_BUFFER_ELEMENT_H__
 
 #include <stdatomic.h>
 #include <stdint.h>
@@ -40,10 +41,7 @@
     const uint32_t mPid;
     const uint32_t mTid;
     log_time mRealTime;
-    union {
-        char* mMsg;    // mDropped == false
-        int32_t mTag;  // mDropped == true
-    };
+    char* mMsg;
     union {
         const uint16_t mMsgLen;  // mDropped == false
         uint16_t mDroppedCount;  // mDropped == true
@@ -95,5 +93,8 @@
     }
 
     static const log_time FLUSH_ERROR;
-    log_time flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
+    log_time flushTo(SocketClient* writer, LogBuffer* parent, bool privileged,
+                     bool lastSame);
 };
+
+#endif
diff --git a/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl b/logd/LogBufferInterface.cpp
similarity index 60%
rename from gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
rename to logd/LogBufferInterface.cpp
index 097bb54..4b6d363 100644
--- a/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
+++ b/logd/LogBufferInterface.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 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,11 +14,16 @@
  * limitations under the License.
  */
 
-package android.service.gatekeeper;
+#include "LogBufferInterface.h"
+#include "LogUtils.h"
 
-/**
- * Response object for a GateKeeper verification request.
- * @hide
- */
-parcelable GateKeeperResponse cpp_header "gatekeeper/GateKeeperResponse.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
new file mode 100644
index 0000000..f31e244
--- /dev/null
+++ b/logd/LogBufferInterface.h
@@ -0,0 +1,43 @@
+/*
+ * 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, uint16_t 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/LogListener.cpp b/logd/LogListener.cpp
index ba61042..2f22778 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,7 +14,9 @@
  * 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>
@@ -30,8 +32,9 @@
 #include "LogListener.h"
 #include "LogUtils.h"
 
-LogListener::LogListener(LogBuffer* buf, LogReader* reader)
-    : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
+LogListener::LogListener(LogBufferInterface* buf, LogReader* reader)
+    : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {
+}
 
 bool LogListener::onDataAvailable(SocketClient* cli) {
     static bool name_set;
@@ -41,7 +44,8 @@
     }
 
     // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
-    char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
+    char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) +
+                LOGGER_ENTRY_MAX_PAYLOAD + 1];
     struct iovec iov = { buffer, sizeof(buffer) - 1 };
 
     alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
@@ -74,8 +78,11 @@
         cmsg = CMSG_NXTHDR(&hdr, cmsg);
     }
 
+    struct ucred fake_cred;
     if (cred == nullptr) {
-        return false;
+        cred = &fake_cred;
+        cred->pid = 0;
+        cred->uid = DEFAULT_OVERFLOWUID;
     }
 
     if (cred->uid == AID_LOGD) {
@@ -99,16 +106,40 @@
         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.
 
-    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));
+    if (logbuf != nullptr) {
+        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 != nullptr) {
+            reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
+        }
     }
 
     return true;
diff --git a/logd/LogListener.h b/logd/LogListener.h
index 8fe3da4..a562a54 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,12 +20,22 @@
 #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 {
-    LogBuffer* logbuf;
+    LogBufferInterface* logbuf;
     LogReader* reader;
 
    public:
-     LogListener(LogBuffer* buf, LogReader* reader);
+    LogListener(LogBufferInterface* buf, LogReader* reader /* nullable */);
 
    protected:
     virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 431b778..116e08e 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -837,12 +837,35 @@
     }
     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
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 0782de3..469f6dc 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -306,10 +306,6 @@
     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;
@@ -389,6 +385,13 @@
           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()),
@@ -784,6 +787,7 @@
     // 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 0cc7886..f19e7b0 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -311,7 +311,9 @@
         if (log_msg.entry.len <= sizeof(uint32_t)) continue;
         uint32_t Tag = get4LE(msg);
         if (Tag != TAG_DEF_LOG_TAG) continue;
-        uid_t uid = log_msg.entry.uid;
+        uid_t uid = (log_msg.entry.hdr_size >= sizeof(logger_entry_v4))
+                        ? log_msg.entry.uid
+                        : AID_ROOT;
 
         std::string Name;
         std::string Format;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fa9f398..4dcd3e7 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -38,6 +38,8 @@
 // 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/README.property b/logd/README.property
index d2a2cbb..da5f96f 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -17,13 +17,10 @@
 					 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/fuzz/Android.bp b/logd/fuzz/Android.bp
deleted file mode 100644
index 299242d..0000000
--- a/logd/fuzz/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-cc_fuzz {
-    name: "log_buffer_log_fuzzer",
-    srcs: [
-        "log_buffer_log_fuzzer.cpp",
-    ],
-    static_libs: [
-        "libbase",
-        "libcutils",
-        "libselinux",
-        "liblog",
-        "liblogd",
-        "libcutils",
-    ],
-    cflags: ["-Werror"],
-}
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
deleted file mode 100644
index 4d1589b..0000000
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.
- */
-#include <string>
-
-#include "../LogBuffer.h"
-#include "../LogTimes.h"
-
-// We don't want to waste a lot of entropy on messages
-#define MAX_MSG_LENGTH 5
-
-// Tag IDs usually start at 1000, we only want to try 1000 through 1009
-#define MIN_TAG_ID 1000
-#define TAG_MOD 10
-
-namespace android {
-struct LogInput {
-  public:
-    log_id_t log_id;
-    log_time realtime;
-    uid_t uid;
-    pid_t pid;
-    pid_t tid;
-    unsigned int log_mask;
-};
-
-int write_log_messages(const uint8_t** pdata, size_t* data_left, LogBuffer* log_buffer) {
-    const uint8_t* data = *pdata;
-    const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
-    data += sizeof(LogInput);
-    *data_left -= sizeof(LogInput);
-
-    uint32_t tag = MIN_TAG_ID + data[0] % TAG_MOD;
-    uint8_t msg_length = data[1] % MAX_MSG_LENGTH;
-    if (msg_length < 2) {
-        // Not enough data for message
-        return 0;
-    }
-
-    data += 2 * sizeof(uint8_t);
-    *data_left -= 2 * sizeof(uint8_t);
-
-    if (*data_left < msg_length) {
-        // Not enough data for tag and message
-        *pdata = data;
-        return 0;
-    }
-
-    // We need nullterm'd strings
-    char msg[sizeof(uint32_t) + MAX_MSG_LENGTH + sizeof(char)];
-    char* msg_only = msg + sizeof(uint32_t);
-    memcpy(msg, &tag, sizeof(uint32_t));
-    memcpy(msg_only, data, msg_length);
-    msg_only[msg_length] = '\0';
-    data += msg_length;
-    *data_left -= msg_length;
-
-    // Other elements not in enum.
-    log_id_t log_id = static_cast<log_id_t>(unsigned(logInput->log_id) % (LOG_ID_MAX + 1));
-    log_buffer->log(log_id, logInput->realtime, logInput->uid, logInput->pid, logInput->tid, msg,
-                    sizeof(uint32_t) + msg_length + 1);
-    log_buffer->formatStatistics(logInput->uid, logInput->pid, logInput->log_mask);
-    *pdata = data;
-    return 1;
-}
-
-// Because system/core/logd/main.cpp redefines these.
-void prdebug(char const* fmt, ...) {
-    va_list ap;
-    va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-}
-char* uidToName(uid_t) {
-    return strdup("fake");
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    // We want a random tag length and a random remaining message length
-    if (data == nullptr || size < sizeof(LogInput) + 2 * sizeof(uint8_t)) {
-        return 0;
-    }
-
-    LastLogTimes times;
-    LogBuffer log_buffer(&times);
-    size_t data_left = size;
-    const uint8_t** pdata = &data;
-
-    log_buffer.enableStatistics();
-    log_buffer.initPrune(nullptr);
-    // We want to get pruning code to get called.
-    log_id_for_each(i) { log_buffer.setSize(i, 10000); }
-
-    while (data_left >= sizeof(LogInput) + 2 * sizeof(uint8_t)) {
-        if (!write_log_messages(pdata, &data_left, &log_buffer)) {
-            return 0;
-        }
-    }
-
-    log_id_for_each(i) { log_buffer.clear(i); }
-    return 0;
-}
-}  // namespace android
diff --git a/logd/logd.rc b/logd/logd.rc
index 530f342..438419a 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -6,8 +6,7 @@
     file /dev/kmsg w
     user logd
     group logd system package_info readproc
-    capabilities SYSLOG AUDIT_CONTROL
-    priority 10
+    capabilities SYSLOG AUDIT_CONTROL SETGID
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 23bbf86..fd3cdf8 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -17,7 +17,6 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <linux/capability.h>
 #include <poll.h>
 #include <sched.h>
 #include <semaphore.h>
@@ -58,10 +57,35 @@
     '<', '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. 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.
+//
+// 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
+//
+
 static int drop_privs(bool klogd, bool auditd) {
     sched_param param = {};
 
@@ -75,6 +99,11 @@
         return -1;
     }
 
+    if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+        android::prdebug("failed to set background cgroup");
+        return -1;
+    }
+
     if (!__android_logger_property_get_bool("ro.debuggable",
                                             BOOL_DEFAULT_FALSE) &&
         prctl(PR_SET_DUMPABLE, 0) == -1) {
@@ -82,26 +111,52 @@
         return -1;
     }
 
-    std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
-    if (cap_clear(caps.get()) < 0) {
+    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) {
         return -1;
     }
-    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) {
+    if (cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(cap_value), cap_value,
+                     CAP_SET) < 0) {
         return -1;
     }
     if (cap_set_proc(caps.get()) < 0) {
-        android::prdebug("failed to set CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
+        android::prdebug(
+            "failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)",
+            errno);
+        return -1;
+    }
+
+    gid_t groups[] = { AID_READPROC };
+
+    if (setgroups(arraysize(groups), groups) == -1) {
+        android::prdebug("failed to set AID_READPROC groups");
+        return -1;
+    }
+
+    if (setgid(AID_LOGD) != 0) {
+        android::prdebug("failed to set AID_LOGD gid");
+        return -1;
+    }
+
+    if (setuid(AID_LOGD) != 0) {
+        android::prdebug("failed to set AID_LOGD uid");
+        return -1;
+    }
+
+    if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) {
+        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);
         return -1;
     }
 
@@ -150,14 +205,67 @@
     }
 }
 
+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',
@@ -194,30 +302,26 @@
     return nullptr;
 }
 
+static sem_t sem_name;
+
 char* android::uidToName(uid_t u) {
-    struct Userdata {
-        uid_t uid;
-        char* name;
-    } userdata = {
-            .uid = u,
-            .name = nullptr,
-    };
+    if (!u || !reinit_running) {
+        return nullptr;
+    }
 
-    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);
+    sem_wait(&sem_name);
 
-    return userdata.name;
+    // 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;
 }
 
 // Serves as a global method to trigger reinitialization
@@ -269,6 +373,11 @@
 }
 
 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;
@@ -331,13 +440,10 @@
         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;
@@ -355,6 +461,12 @@
         pthread_attr_destroy(&attr);
     }
 
+    bool auditd =
+        __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
+    if (drop_privs(klogd, auditd) != 0) {
+        return EXIT_FAILURE;
+    }
+
     // 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.
diff --git a/logd/tests/Android.bp b/logd/tests/Android.bp
index d39da8a..83a194f 100644
--- a/logd/tests/Android.bp
+++ b/logd/tests/Android.bp
@@ -35,12 +35,12 @@
 
     srcs: ["logd_test.cpp"],
 
-    static_libs: [
+    shared_libs: [
         "libbase",
         "libcutils",
         "libselinux",
-        "liblog",
     ],
+    static_libs: ["liblog"],
 }
 
 // Build tests for the logger. Run with:
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index f47bee1..b6c33d7 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -241,18 +241,47 @@
 static void caught_signal(int /* signum */) {
 }
 
-static void dump_log_msg(const char* prefix, log_msg* msg, int lid) {
+static void dump_log_msg(const char* prefix, log_msg* msg, unsigned int version,
+                         int lid) {
     std::cout << std::flush;
     std::cerr << std::flush;
     fflush(stdout);
     fflush(stderr);
-    EXPECT_EQ(sizeof(logger_entry), msg->entry.hdr_size);
+    switch (msg->entry.hdr_size) {
+        case 0:
+            version = 1;
+            break;
 
-    fprintf(stderr, "%s: [%u] ", prefix, msg->len());
-    fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
-    fprintf(stderr, "pid=%u tid=%u %u.%09u ", msg->entry.pid, msg->entry.tid, msg->entry.sec,
-            msg->entry.nsec);
-    lid = msg->entry.lid;
+        case sizeof(msg->entry_v2): /* PLUS case sizeof(msg->entry_v3): */
+            if (version == 0) {
+                version = (msg->entry_v3.lid < LOG_ID_MAX) ? 3 : 2;
+            }
+            break;
+
+        case sizeof(msg->entry_v4):
+            if (version == 0) {
+                version = 4;
+            }
+            break;
+    }
+
+    fprintf(stderr, "%s: v%u[%u] ", prefix, version, msg->len());
+    if (version != 1) {
+        fprintf(stderr, "hdr_size=%u ", msg->entry.hdr_size);
+    }
+    fprintf(stderr, "pid=%u tid=%u %u.%09u ", msg->entry.pid, msg->entry.tid,
+            msg->entry.sec, msg->entry.nsec);
+    switch (version) {
+        case 1:
+            break;
+        case 2:
+            fprintf(stderr, "euid=%u ", msg->entry_v2.euid);
+            break;
+        case 3:
+        default:
+            lid = msg->entry.lid;
+            break;
+    }
 
     switch (lid) {
         case 0:
@@ -320,6 +349,86 @@
 }
 #endif
 
+TEST(logd, both) {
+#ifdef __ANDROID__
+    log_msg msg;
+
+    // check if we can read any logs from logd
+    bool user_logger_available = false;
+    bool user_logger_content = false;
+
+    int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                 SOCK_SEQPACKET);
+    if (fd >= 0) {
+        struct sigaction ignore, old_sigaction;
+        memset(&ignore, 0, sizeof(ignore));
+        ignore.sa_handler = caught_signal;
+        sigemptyset(&ignore.sa_mask);
+        sigaction(SIGALRM, &ignore, &old_sigaction);
+        unsigned int old_alarm = alarm(10);
+
+        static const char ask[] = "dumpAndClose lids=0,1,2,3";
+        user_logger_available = write(fd, ask, sizeof(ask)) == sizeof(ask);
+
+        user_logger_content = recv(fd, msg.buf, sizeof(msg), 0) > 0;
+
+        if (user_logger_content) {
+            dump_log_msg("user", &msg, 3, -1);
+        }
+
+        alarm(old_alarm);
+        sigaction(SIGALRM, &old_sigaction, nullptr);
+
+        close(fd);
+    }
+
+    // check if we can read any logs from kernel logger
+    bool kernel_logger_available = false;
+    bool kernel_logger_content = false;
+
+    static const char* loggers[] = {
+        "/dev/log/main",   "/dev/log_main",   "/dev/log/radio",
+        "/dev/log_radio",  "/dev/log/events", "/dev/log_events",
+        "/dev/log/system", "/dev/log_system",
+    };
+
+    for (unsigned int i = 0; i < arraysize(loggers); ++i) {
+        fd = open(loggers[i], O_RDONLY);
+        if (fd < 0) {
+            continue;
+        }
+        kernel_logger_available = true;
+        fcntl(fd, F_SETFL, O_RDONLY | O_NONBLOCK);
+        int result = TEMP_FAILURE_RETRY(read(fd, msg.buf, sizeof(msg)));
+        if (result > 0) {
+            kernel_logger_content = true;
+            dump_log_msg("kernel", &msg, 0, i / 2);
+        }
+        close(fd);
+    }
+
+    static const char yes[] = "\xE2\x9C\x93";
+    static const char no[] = "\xE2\x9c\x98";
+    fprintf(stderr,
+            "LOGGER  Available  Content\n"
+            "user    %-13s%s\n"
+            "kernel  %-13s%s\n"
+            " status %-11s%s\n",
+            (user_logger_available) ? yes : no, (user_logger_content) ? yes : no,
+            (kernel_logger_available) ? yes : no,
+            (kernel_logger_content) ? yes : no,
+            (user_logger_available && kernel_logger_available) ? "ERROR" : "ok",
+            (user_logger_content && kernel_logger_content) ? "ERROR" : "ok");
+
+    EXPECT_EQ(0, user_logger_available && kernel_logger_available);
+    EXPECT_EQ(0, !user_logger_available && !kernel_logger_available);
+    EXPECT_EQ(0, user_logger_content && kernel_logger_content);
+    EXPECT_EQ(0, !user_logger_content && !kernel_logger_content);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
 #ifdef __ANDROID__
 // BAD ROBOT
 //   Benchmark threshold are generally considered bad form unless there is
@@ -555,11 +664,11 @@
     }
 
     if (content_wrap) {
-        dump_log_msg("wrap", &msg_wrap, -1);
+        dump_log_msg("wrap", &msg_wrap, 3, -1);
     }
 
     if (content_timeout) {
-        dump_log_msg("timeout", &msg_timeout, -1);
+        dump_log_msg("timeout", &msg_timeout, 3, -1);
     }
 
     EXPECT_TRUE(written);
@@ -692,11 +801,11 @@
     }
 
     if (content_wrap) {
-        dump_log_msg("wrap", &msg_wrap, -1);
+        dump_log_msg("wrap", &msg_wrap, 3, -1);
     }
 
     if (content_timeout) {
-        dump_log_msg("timeout", &msg_timeout, -1);
+        dump_log_msg("timeout", &msg_timeout, 3, -1);
     }
 
     if (content_wrap || !content_timeout) {
@@ -747,7 +856,7 @@
 
     EXPECT_TRUE(read_one);
     if (read_one) {
-        dump_log_msg("user", &msg, -1);
+        dump_log_msg("user", &msg, 3, -1);
     }
 
     fprintf(stderr, "Sleep for >%d seconds logd SO_SNDTIMEO ...\n", sndtimeo);
@@ -765,7 +874,7 @@
 
     EXPECT_EQ(0, recv_ret);
     if (recv_ret > 0) {
-        dump_log_msg("user", &msg, -1);
+        dump_log_msg("user", &msg, 3, -1);
     }
     EXPECT_EQ(0, save_errno);
 
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index 8851a47..c378646 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -13,12 +13,11 @@
     name: "liblogwrap",
     defaults: ["logwrapper_defaults"],
     recovery_available: true,
-    srcs: ["logwrap.cpp"],
+    srcs: ["logwrap.c"],
     shared_libs: [
         "libcutils",
         "liblog",
     ],
-    header_libs: ["libbase_headers"],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
 }
@@ -32,10 +31,9 @@
     defaults: ["logwrapper_defaults"],
     local_include_dirs: ["include"],
     srcs: [
-        "logwrap.cpp",
-        "logwrapper.cpp",
+        "logwrap.c",
+        "logwrapper.c",
     ],
-    header_libs: ["libbase_headers"],
     shared_libs: ["libcutils", "liblog"],
 }
 
@@ -56,10 +54,10 @@
 // ========================================================
 
 cc_benchmark {
-    name: "logwrap_fork_execvp_benchmark",
+    name: "android_fork_execvp_ext_benchmark",
     defaults: ["logwrapper_defaults"],
     srcs: [
-        "logwrap_fork_execvp_benchmark.cpp",
+        "android_fork_execvp_ext_benchmark.cpp",
     ],
     shared_libs: [
         "libbase",
diff --git a/logwrapper/logwrap_fork_execvp_benchmark.cpp b/logwrapper/android_fork_execvp_ext_benchmark.cpp
similarity index 81%
rename from logwrapper/logwrap_fork_execvp_benchmark.cpp
rename to logwrapper/android_fork_execvp_ext_benchmark.cpp
index b2d0c71..1abd932 100644
--- a/logwrapper/logwrap_fork_execvp_benchmark.cpp
+++ b/logwrapper/android_fork_execvp_ext_benchmark.cpp
@@ -23,7 +23,9 @@
     const char* argv[] = {"/system/bin/echo", "hello", "world"};
     const int argc = 3;
     while (state.KeepRunning()) {
-        int rc = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_NONE, false, nullptr);
+        int rc = android_fork_execvp_ext(
+            argc, (char**)argv, NULL /* status */, false /* ignore_int_quit */, LOG_NONE,
+            false /* abbreviated */, NULL /* file_path */, NULL /* opts */, 0 /* opts_len */);
         CHECK_EQ(0, rc);
     }
 }
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index cb40ee2..d3538b3 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -15,11 +15,23 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef __LIBS_LOGWRAP_H
+#define __LIBS_LOGWRAP_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+__BEGIN_DECLS
 
 /*
  * Run a command while logging its stdout and stderr
  *
+ * WARNING: while this function is running it will clear all SIGCHLD handlers
+ * if you rely on SIGCHLD in the caller there is a chance zombies will be
+ * created if you're not calling waitpid after calling this. This function will
+ * log a warning when it clears SIGCHLD for processes other than the child it
+ * created.
+ *
  * Arguments:
  *   argc:   the number of elements in argv
  *   argv:   an array of strings containing the command to be executed and its
@@ -28,10 +40,10 @@
  *   status: the equivalent child status as populated by wait(status). This
  *           value is only valid when logwrap successfully completes. If NULL
  *           the return value of the child will be the function's return value.
- *   forward_signals: set to true if you want to forward SIGINT, SIGQUIT, and
- *           SIGHUP to the child process, while it is running.  You likely do
- *           not need to use this; it is primarily for the logwrapper
- *           executable itself.
+ *   ignore_int_quit: set to true if you want to completely ignore SIGINT and
+ *           SIGQUIT while logwrap is running. This may force the end-user to
+ *           send a signal twice to signal the caller (once for the child, and
+ *           once for the caller)
  *   log_target: Specify where to log the output of the child, either LOG_NONE,
  *           LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel
  *           log), or LOG_FILE (and you need to specify a pathname in the
@@ -42,6 +54,8 @@
  *           the specified log until the child has exited.
  *   file_path: if log_target has the LOG_FILE bit set, then this parameter
  *           must be set to the pathname of the file to log to.
+ *   unused_opts: currently unused.
+ *   unused_opts_len: currently unused.
  *
  * Return value:
  *   0 when logwrap successfully run the child process and captured its status
@@ -51,11 +65,29 @@
  *
  */
 
-/* Values for the log_target parameter logwrap_fork_execvp() */
+/* Values for the log_target parameter android_fork_execvp_ext() */
 #define LOG_NONE        0
 #define LOG_ALOG        1
 #define LOG_KLOG        2
 #define LOG_FILE        4
 
-int logwrap_fork_execvp(int argc, const char* const* argv, int* status, bool forward_signals,
-                        int log_target, bool abbreviated, const char* file_path);
+// TODO: Remove unused_opts / unused_opts_len in a followup change.
+int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
+        int log_target, bool abbreviated, char *file_path, void* unused_opts,
+        int unused_opts_len);
+
+/* Similar to above, except abbreviated logging is not available, and if logwrap
+ * is true, logging is to the Android system log, and if false, there is no
+ * logging.
+ */
+static inline int android_fork_execvp(int argc, char* argv[], int *status,
+                                     bool ignore_int_quit, bool logwrap)
+{
+    return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
+                                   (logwrap ? LOG_ALOG : LOG_NONE), false, NULL,
+                                   NULL, 0);
+}
+
+__END_DECLS
+
+#endif /* __LIBS_LOGWRAP_H */
diff --git a/logwrapper/logwrap.cpp b/logwrapper/logwrap.c
similarity index 62%
rename from logwrapper/logwrap.cpp
rename to logwrapper/logwrap.c
index 5337801..8621993 100644
--- a/logwrapper/logwrap.cpp
+++ b/logwrapper/logwrap.c
@@ -19,6 +19,7 @@
 #include <libgen.h>
 #include <poll.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -27,31 +28,26 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <algorithm>
-
-#include <android-base/macros.h>
 #include <cutils/klog.h>
 #include <log/log.h>
 #include <logwrap/logwrap.h>
 
+#define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
 static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER;
-// Protected by fd_mutex.  These signals must be blocked while modifying as well.
-static pid_t child_pid;
-static struct sigaction old_int;
-static struct sigaction old_quit;
-static struct sigaction old_hup;
 
-#define ERROR(fmt, args...)                         \
-    do {                                            \
-        fprintf(stderr, fmt, ##args);               \
-        ALOG(LOG_ERROR, "logwrapper", fmt, ##args); \
-    } while (0)
+#define ERROR(fmt, args...)                                                   \
+do {                                                                          \
+    fprintf(stderr, fmt, ## args);                                            \
+    ALOG(LOG_ERROR, "logwrapper", fmt, ## args);                              \
+} while(0)
 
-#define FATAL_CHILD(fmt, args...) \
-    do {                          \
-        ERROR(fmt, ##args);       \
-        _exit(-1);                \
-    } while (0)
+#define FATAL_CHILD(fmt, args...)                                             \
+do {                                                                          \
+    ERROR(fmt, ## args);                                                      \
+    _exit(-1);                                                                \
+} while(0)
 
 #define MAX_KLOG_TAG 16
 
@@ -60,7 +56,7 @@
  */
 #define BEGINNING_BUF_SIZE 0x1000
 struct beginning_buf {
-    char* buf;
+    char *buf;
     size_t alloc_len;
     /* buf_size is the usable space, which is one less than the allocated size */
     size_t buf_size;
@@ -73,7 +69,7 @@
  */
 #define ENDING_BUF_SIZE 0x1000
 struct ending_buf {
-    char* buf;
+    char *buf;
     ssize_t alloc_len;
     /* buf_size is the usable space, which is one less than the allocated size */
     ssize_t buf_size;
@@ -83,7 +79,7 @@
     int write;
 };
 
-/* A structure to hold all the abbreviated buf data */
+ /* A structure to hold all the abbreviated buf data */
 struct abbr_buf {
     struct beginning_buf b_buf;
     struct ending_buf e_buf;
@@ -94,17 +90,19 @@
 struct log_info {
     int log_target;
     char klog_fmt[MAX_KLOG_TAG * 2];
-    const char* btag;
+    char *btag;
     bool abbreviated;
-    FILE* fp;
+    FILE *fp;
     struct abbr_buf a_buf;
 };
 
 /* Forware declaration */
-static void add_line_to_abbr_buf(struct abbr_buf* a_buf, char* linebuf, int linelen);
+static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen);
 
 /* Return 0 on success, and 1 when full */
-static int add_line_to_linear_buf(struct beginning_buf* b_buf, char* line, ssize_t line_len) {
+static int add_line_to_linear_buf(struct beginning_buf *b_buf,
+                                   char *line, ssize_t line_len)
+{
     int full = 0;
 
     if ((line_len + b_buf->used_len) > b_buf->buf_size) {
@@ -118,18 +116,20 @@
     return full;
 }
 
-static void add_line_to_circular_buf(struct ending_buf* e_buf, char* line, ssize_t line_len) {
+static void add_line_to_circular_buf(struct ending_buf *e_buf,
+                                     char *line, ssize_t line_len)
+{
     ssize_t free_len;
     ssize_t needed_space;
     int cnt;
 
-    if (e_buf->buf == nullptr) {
+    if (e_buf->buf == NULL) {
         return;
     }
 
-    if (line_len > e_buf->buf_size) {
-        return;
-    }
+   if (line_len > e_buf->buf_size) {
+       return;
+   }
 
     free_len = e_buf->buf_size - e_buf->used_len;
 
@@ -144,7 +144,7 @@
     /* Copy the line into the circular buffer, dealing with possible
      * wraparound.
      */
-    cnt = std::min(line_len, e_buf->buf_size - e_buf->write);
+    cnt = MIN(line_len, e_buf->buf_size - e_buf->write);
     memcpy(e_buf->buf + e_buf->write, line, cnt);
     if (cnt < line_len) {
         memcpy(e_buf->buf, line + cnt, line_len - cnt);
@@ -154,7 +154,7 @@
 }
 
 /* Log directly to the specified log */
-static void do_log_line(struct log_info* log_info, const char* line) {
+static void do_log_line(struct log_info *log_info, char *line) {
     if (log_info->log_target & LOG_KLOG) {
         klog_write(6, log_info->klog_fmt, line);
     }
@@ -169,7 +169,7 @@
 /* Log to either the abbreviated buf, or directly to the specified log
  * via do_log_line() above.
  */
-static void log_line(struct log_info* log_info, char* line, int len) {
+static void log_line(struct log_info *log_info, char *line, int len) {
     if (log_info->abbreviated) {
         add_line_to_abbr_buf(&log_info->a_buf, line, len);
     } else {
@@ -184,8 +184,9 @@
  * than buf_size (the usable size of the buffer) to make sure there is
  * room to temporarily stuff a null byte to terminate a line for logging.
  */
-static void print_buf_lines(struct log_info* log_info, char* buf, int buf_size) {
-    char* line_start;
+static void print_buf_lines(struct log_info *log_info, char *buf, int buf_size)
+{
+    char *line_start;
     char c;
     int i;
 
@@ -211,17 +212,17 @@
      */
 }
 
-static void init_abbr_buf(struct abbr_buf* a_buf) {
-    char* new_buf;
+static void init_abbr_buf(struct abbr_buf *a_buf) {
+    char *new_buf;
 
     memset(a_buf, 0, sizeof(struct abbr_buf));
-    new_buf = static_cast<char*>(malloc(BEGINNING_BUF_SIZE));
+    new_buf = malloc(BEGINNING_BUF_SIZE);
     if (new_buf) {
         a_buf->b_buf.buf = new_buf;
         a_buf->b_buf.alloc_len = BEGINNING_BUF_SIZE;
         a_buf->b_buf.buf_size = BEGINNING_BUF_SIZE - 1;
     }
-    new_buf = static_cast<char*>(malloc(ENDING_BUF_SIZE));
+    new_buf = malloc(ENDING_BUF_SIZE);
     if (new_buf) {
         a_buf->e_buf.buf = new_buf;
         a_buf->e_buf.alloc_len = ENDING_BUF_SIZE;
@@ -229,22 +230,23 @@
     }
 }
 
-static void free_abbr_buf(struct abbr_buf* a_buf) {
+static void free_abbr_buf(struct abbr_buf *a_buf) {
     free(a_buf->b_buf.buf);
     free(a_buf->e_buf.buf);
 }
 
-static void add_line_to_abbr_buf(struct abbr_buf* a_buf, char* linebuf, int linelen) {
+static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen) {
     if (!a_buf->beginning_buf_full) {
-        a_buf->beginning_buf_full = add_line_to_linear_buf(&a_buf->b_buf, linebuf, linelen);
+        a_buf->beginning_buf_full =
+            add_line_to_linear_buf(&a_buf->b_buf, linebuf, linelen);
     }
     if (a_buf->beginning_buf_full) {
         add_line_to_circular_buf(&a_buf->e_buf, linebuf, linelen);
     }
 }
 
-static void print_abbr_buf(struct log_info* log_info) {
-    struct abbr_buf* a_buf = &log_info->a_buf;
+static void print_abbr_buf(struct log_info *log_info) {
+    struct abbr_buf *a_buf = &log_info->a_buf;
 
     /* Add the abbreviated output to the kernel log */
     if (a_buf->b_buf.alloc_len) {
@@ -267,13 +269,14 @@
      * and then cal print_buf_lines on it */
     if (a_buf->e_buf.read < a_buf->e_buf.write) {
         /* no wrap around, just print it */
-        print_buf_lines(log_info, a_buf->e_buf.buf + a_buf->e_buf.read, a_buf->e_buf.used_len);
+        print_buf_lines(log_info, a_buf->e_buf.buf + a_buf->e_buf.read,
+                        a_buf->e_buf.used_len);
     } else {
         /* The circular buffer will always have at least 1 byte unused,
          * so by allocating alloc_len here we will have at least
          * 1 byte of space available as required by print_buf_lines().
          */
-        char* nbuf = static_cast<char*>(malloc(a_buf->e_buf.alloc_len));
+        char * nbuf = malloc(a_buf->e_buf.alloc_len);
         if (!nbuf) {
             return;
         }
@@ -286,54 +289,15 @@
     }
 }
 
-static void signal_handler(int signal_num);
-
-static void block_signals(sigset_t* oldset) {
-    sigset_t blockset;
-
-    sigemptyset(&blockset);
-    sigaddset(&blockset, SIGINT);
-    sigaddset(&blockset, SIGQUIT);
-    sigaddset(&blockset, SIGHUP);
-    pthread_sigmask(SIG_BLOCK, &blockset, oldset);
-}
-
-static void unblock_signals(sigset_t* oldset) {
-    pthread_sigmask(SIG_SETMASK, oldset, nullptr);
-}
-
-static void setup_signal_handlers(pid_t pid) {
-    struct sigaction handler = {.sa_handler = signal_handler};
-
-    child_pid = pid;
-    sigaction(SIGINT, &handler, &old_int);
-    sigaction(SIGQUIT, &handler, &old_quit);
-    sigaction(SIGHUP, &handler, &old_hup);
-}
-
-static void restore_signal_handlers() {
-    sigaction(SIGINT, &old_int, nullptr);
-    sigaction(SIGQUIT, &old_quit, nullptr);
-    sigaction(SIGHUP, &old_hup, nullptr);
-    child_pid = 0;
-}
-
-static void signal_handler(int signal_num) {
-    if (child_pid == 0 || kill(child_pid, signal_num) != 0) {
-        restore_signal_handlers();
-        raise(signal_num);
-    }
-}
-
-static int parent(const char* tag, int parent_read, pid_t pid, int* chld_sts, int log_target,
-                  bool abbreviated, const char* file_path, bool forward_signals) {
+static int parent(const char *tag, int parent_read, pid_t pid,
+        int *chld_sts, int log_target, bool abbreviated, char *file_path) {
     int status = 0;
     char buffer[4096];
     struct pollfd poll_fds[] = {
-            {
-                    .fd = parent_read,
-                    .events = POLLIN,
-            },
+        [0] = {
+            .fd = parent_read,
+            .events = POLLIN,
+        },
     };
     int rc = 0;
     int fd;
@@ -344,16 +308,11 @@
     int b = 0;  // end index of unprocessed data
     int sz;
     bool found_child = false;
-    // There is a very small chance that opening child_ptty in the child will fail, but in this case
-    // POLLHUP will not be generated below.  Therefore, we use a 1 second timeout for poll() until
-    // we receive a message from child_ptty.  If this times out, we call waitpid() with WNOHANG to
-    // check the status of the child process and exit appropriately if it has terminated.
-    bool received_messages = false;
     char tmpbuf[256];
 
     log_info.btag = basename(tag);
     if (!log_info.btag) {
-        log_info.btag = tag;
+        log_info.btag = (char*) tag;
     }
 
     if (abbreviated && (log_target == LOG_NONE)) {
@@ -364,8 +323,8 @@
     }
 
     if (log_target & LOG_KLOG) {
-        snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt), "<6>%.*s: %%s\n", MAX_KLOG_TAG,
-                 log_info.btag);
+        snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt),
+                 "<6>%.*s: %%s\n", MAX_KLOG_TAG, log_info.btag);
     }
 
     if ((log_target & LOG_FILE) && !file_path) {
@@ -388,16 +347,15 @@
     log_info.abbreviated = abbreviated;
 
     while (!found_child) {
-        int timeout = received_messages ? -1 : 1000;
-        if (TEMP_FAILURE_RETRY(poll(poll_fds, arraysize(poll_fds), timeout)) < 0) {
+        if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) {
             ERROR("poll failed\n");
             rc = -1;
             goto err_poll;
         }
 
         if (poll_fds[0].revents & POLLIN) {
-            received_messages = true;
-            sz = TEMP_FAILURE_RETRY(read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
+            sz = TEMP_FAILURE_RETRY(
+                read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
 
             sz += b;
             // Log one line at a time
@@ -438,20 +396,10 @@
             }
         }
 
-        if (!received_messages || (poll_fds[0].revents & POLLHUP)) {
+        if (poll_fds[0].revents & POLLHUP) {
             int ret;
-            sigset_t oldset;
 
-            if (forward_signals) {
-                // Our signal handlers forward these signals to 'child_pid', but waitpid() may reap
-                // the child, so we must block these signals until we either 1) conclude that the
-                // child is still running or 2) determine the child has been reaped and we have
-                // reset the signals to their original disposition.
-                block_signals(&oldset);
-            }
-
-            int flags = (poll_fds[0].revents & POLLHUP) ? 0 : WNOHANG;
-            ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, flags));
+            ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
             if (ret < 0) {
                 rc = errno;
                 ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
@@ -460,29 +408,22 @@
             if (ret > 0) {
                 found_child = true;
             }
-
-            if (forward_signals) {
-                if (found_child) {
-                    restore_signal_handlers();
-                }
-                unblock_signals(&oldset);
-            }
         }
     }
 
-    if (chld_sts != nullptr) {
+    if (chld_sts != NULL) {
         *chld_sts = status;
     } else {
-        if (WIFEXITED(status))
-            rc = WEXITSTATUS(status);
-        else
-            rc = -ECHILD;
+      if (WIFEXITED(status))
+        rc = WEXITSTATUS(status);
+      else
+        rc = -ECHILD;
     }
 
     // Flush remaining data
     if (a != b) {
-        buffer[b] = '\0';
-        log_line(&log_info, &buffer[a], b - a);
+      buffer[b] = '\0';
+      log_line(&log_info, &buffer[a], b - a);
     }
 
     /* All the output has been processed, time to dump the abbreviated output */
@@ -491,21 +432,21 @@
     }
 
     if (WIFEXITED(status)) {
-        if (WEXITSTATUS(status)) {
-            snprintf(tmpbuf, sizeof(tmpbuf), "%s terminated by exit(%d)\n", log_info.btag,
-                     WEXITSTATUS(status));
-            do_log_line(&log_info, tmpbuf);
-        }
+      if (WEXITSTATUS(status)) {
+        snprintf(tmpbuf, sizeof(tmpbuf),
+                 "%s terminated by exit(%d)\n", log_info.btag, WEXITSTATUS(status));
+        do_log_line(&log_info, tmpbuf);
+      }
     } else {
-        if (WIFSIGNALED(status)) {
-            snprintf(tmpbuf, sizeof(tmpbuf), "%s terminated by signal %d\n", log_info.btag,
-                     WTERMSIG(status));
-            do_log_line(&log_info, tmpbuf);
-        } else if (WIFSTOPPED(status)) {
-            snprintf(tmpbuf, sizeof(tmpbuf), "%s stopped by signal %d\n", log_info.btag,
-                     WSTOPSIG(status));
-            do_log_line(&log_info, tmpbuf);
-        }
+      if (WIFSIGNALED(status)) {
+        snprintf(tmpbuf, sizeof(tmpbuf),
+                       "%s terminated by signal %d\n", log_info.btag, WTERMSIG(status));
+        do_log_line(&log_info, tmpbuf);
+      } else if (WIFSTOPPED(status)) {
+        snprintf(tmpbuf, sizeof(tmpbuf),
+                       "%s stopped by signal %d\n", log_info.btag, WSTOPSIG(status));
+        do_log_line(&log_info, tmpbuf);
+      }
     }
 
 err_waitpid:
@@ -519,24 +460,33 @@
     return rc;
 }
 
-static void child(int argc, const char* const* argv) {
+static void child(int argc, char* argv[]) {
     // create null terminated argv_child array
     char* argv_child[argc + 1];
-    memcpy(argv_child, argv, argc * sizeof(char*));
-    argv_child[argc] = nullptr;
+    memcpy(argv_child, argv, argc * sizeof(char *));
+    argv_child[argc] = NULL;
 
     if (execvp(argv_child[0], argv_child)) {
-        FATAL_CHILD("executing %s failed: %s\n", argv_child[0], strerror(errno));
+        FATAL_CHILD("executing %s failed: %s\n", argv_child[0],
+                strerror(errno));
     }
 }
 
-int logwrap_fork_execvp(int argc, const char* const* argv, int* status, bool forward_signals,
-                        int log_target, bool abbreviated, const char* file_path) {
+int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
+        int log_target, bool abbreviated, char *file_path,
+        void *unused_opts, int unused_opts_len) {
     pid_t pid;
     int parent_ptty;
+    int child_ptty;
+    struct sigaction intact;
+    struct sigaction quitact;
+    sigset_t blockset;
     sigset_t oldset;
     int rc = 0;
 
+    LOG_ALWAYS_FATAL_IF(unused_opts != NULL);
+    LOG_ALWAYS_FATAL_IF(unused_opts_len != 0);
+
     rc = pthread_mutex_lock(&fd_mutex);
     if (rc) {
         ERROR("failed to lock signal_fd mutex\n");
@@ -544,7 +494,7 @@
     }
 
     /* Use ptty instead of socketpair so that STDOUT is not buffered */
-    parent_ptty = TEMP_FAILURE_RETRY(posix_openpt(O_RDWR | O_CLOEXEC));
+    parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR));
     if (parent_ptty < 0) {
         ERROR("Cannot create parent ptty\n");
         rc = -1;
@@ -553,34 +503,33 @@
 
     char child_devname[64];
     if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
-        ptsname_r(parent_ptty, child_devname, sizeof(child_devname)) != 0) {
+            ptsname_r(parent_ptty, child_devname, sizeof(child_devname)) != 0) {
         ERROR("Problem with /dev/ptmx\n");
         rc = -1;
         goto err_ptty;
     }
 
-    if (forward_signals) {
-        // Block these signals until we have the child pid and our signal handlers set up.
-        block_signals(&oldset);
+    child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR));
+    if (child_ptty < 0) {
+        ERROR("Cannot open child_ptty\n");
+        rc = -1;
+        goto err_child_ptty;
     }
 
+    sigemptyset(&blockset);
+    sigaddset(&blockset, SIGINT);
+    sigaddset(&blockset, SIGQUIT);
+    pthread_sigmask(SIG_BLOCK, &blockset, &oldset);
+
     pid = fork();
     if (pid < 0) {
+        close(child_ptty);
         ERROR("Failed to fork\n");
         rc = -1;
         goto err_fork;
     } else if (pid == 0) {
         pthread_mutex_unlock(&fd_mutex);
-        if (forward_signals) {
-            unblock_signals(&oldset);
-        }
-
-        setsid();
-
-        int child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR | O_CLOEXEC));
-        if (child_ptty < 0) {
-            FATAL_CHILD("Cannot open child_ptty: %s\n", strerror(errno));
-        }
+        pthread_sigmask(SIG_SETMASK, &oldset, NULL);
         close(parent_ptty);
 
         dup2(child_ptty, 1);
@@ -589,23 +538,27 @@
 
         child(argc, argv);
     } else {
-        if (forward_signals) {
-            setup_signal_handlers(pid);
-            unblock_signals(&oldset);
+        close(child_ptty);
+        if (ignore_int_quit) {
+            struct sigaction ignact;
+
+            memset(&ignact, 0, sizeof(ignact));
+            ignact.sa_handler = SIG_IGN;
+            sigaction(SIGINT, &ignact, &intact);
+            sigaction(SIGQUIT, &ignact, &quitact);
         }
 
-        rc = parent(argv[0], parent_ptty, pid, status, log_target, abbreviated, file_path,
-                    forward_signals);
-
-        if (forward_signals) {
-            restore_signal_handlers();
-        }
+        rc = parent(argv[0], parent_ptty, pid, status, log_target,
+                    abbreviated, file_path);
     }
 
+    if (ignore_int_quit) {
+        sigaction(SIGINT, &intact, NULL);
+        sigaction(SIGQUIT, &quitact, NULL);
+    }
 err_fork:
-    if (forward_signals) {
-        unblock_signals(&oldset);
-    }
+    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+err_child_ptty:
 err_ptty:
     close(parent_ptty);
 err_open:
diff --git a/logwrapper/logwrapper.cpp b/logwrapper/logwrapper.c
similarity index 62%
rename from logwrapper/logwrapper.cpp
rename to logwrapper/logwrapper.c
index 7118d12..33454c6 100644
--- a/logwrapper/logwrapper.cpp
+++ b/logwrapper/logwrapper.c
@@ -24,26 +24,27 @@
 #include <log/log.h>
 #include <logwrap/logwrap.h>
 
-void fatal(const char* msg) {
+void fatal(const char *msg) {
     fprintf(stderr, "%s", msg);
     ALOG(LOG_ERROR, "logwrapper", "%s", msg);
     exit(-1);
 }
 
 void usage() {
-    fatal("Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...]\n"
-          "\n"
-          "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
-          "the Android logging system. Tag is set to BINARY, priority is\n"
-          "always LOG_INFO.\n"
-          "\n"
-          "-a: Causes logwrapper to do abbreviated logging.\n"
-          "    This logs up to the first 4K and last 4K of the command\n"
-          "    being run, and logs the output when the command exits\n"
-          "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
-          "    fault address is set to the status of wait()\n"
-          "-k: Causes logwrapper to log to the kernel log instead of\n"
-          "    the Android system log\n");
+    fatal(
+        "Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...]\n"
+        "\n"
+        "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
+        "the Android logging system. Tag is set to BINARY, priority is\n"
+        "always LOG_INFO.\n"
+        "\n"
+        "-a: Causes logwrapper to do abbreviated logging.\n"
+        "    This logs up to the first 4K and last 4K of the command\n"
+        "    being run, and logs the output when the command exits\n"
+        "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
+        "    fault address is set to the status of wait()\n"
+        "-k: Causes logwrapper to log to the kernel log instead of\n"
+        "    the Android system log\n");
 }
 
 int main(int argc, char* argv[]) {
@@ -68,7 +69,7 @@
                 break;
             case '?':
             default:
-                usage();
+              usage();
         }
     }
     argc -= optind;
@@ -78,7 +79,8 @@
         usage();
     }
 
-    rc = logwrap_fork_execvp(argc, &argv[0], &status, true, log_target, abbreviated, nullptr);
+    rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
+                                 log_target, abbreviated, NULL, NULL, 0);
     if (!rc) {
         if (WIFEXITED(status))
             rc = WEXITSTATUS(status);
@@ -87,8 +89,8 @@
     }
 
     if (seg_fault_on_exit) {
-        uintptr_t fault_address = (uintptr_t)status;
-        *(int*)fault_address = 0;  // causes SIGSEGV with fault_address = status
+        uintptr_t fault_address = (uintptr_t) status;
+        *(int *) fault_address = 0;  // causes SIGSEGV with fault_address = status
     }
 
     return rc;
diff --git a/mkbootimg/Android.bp b/mkbootimg/Android.bp
new file mode 100644
index 0000000..c3cf746
--- /dev/null
+++ b/mkbootimg/Android.bp
@@ -0,0 +1,64 @@
+// Copyright 2012 The Android Open Source Project
+
+cc_library_headers {
+    name: "libmkbootimg_abi_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
+
+cc_library_headers {
+    name: "bootimg_headers",
+    vendor_available: true,
+    recovery_available: true,
+    export_include_dirs: ["include/bootimg"],
+    host_supported: true,
+    target: {
+        windows: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libmkbootimg_abi_check",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "mkbootimg_dummy.cpp",
+    ],
+    header_libs: ["libmkbootimg_abi_headers"],
+    export_header_lib_headers: ["libmkbootimg_abi_headers"],
+}
+
+python_defaults {
+    name: "mkbootimg_defaults",
+
+    version: {
+        py2: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+        py3: {
+            enabled: false,
+            embedded_launcher: false,
+        },
+    },
+}
+
+python_binary_host {
+    name: "mkbootimg",
+    defaults: ["mkbootimg_defaults"],
+    srcs: [
+        "mkbootimg.py",
+    ],
+}
+
+python_binary_host {
+    name: "unpack_bootimg",
+    defaults: ["mkbootimg_defaults"],
+    srcs: [
+        "unpack_bootimg.py",
+    ],
+}
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
new file mode 100644
index 0000000..3a00860
--- /dev/null
+++ b/mkbootimg/OWNERS
@@ -0,0 +1,2 @@
+hridya@google.com
+tbao@google.com
diff --git a/mkbootimg/include/abi_check/mkbootimg_abi_check.h b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
new file mode 100644
index 0000000..d478aba
--- /dev/null
+++ b/mkbootimg/include/abi_check/mkbootimg_abi_check.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <bootimg/bootimg.h>
+
+// This header has been created for the following reaons:
+//    1) In order for a change in a user defined type to be classified as API /
+//       ABI breaking, it needs to be referenced by an 'exported interface'
+//       (in this case the function mkbootimg_dummy).
+//    2) Since 'mkbootimg_dummy' needs to be exported, we need to have it
+//       exposed through a public header.
+//    3) It is desirable not to pollute bootimg.h with interfaces which are not
+//       'used' in reality by on device binaries. Furthermore, bootimg.h might
+//       be exported by a library in the future, so we must avoid polluting it.
+void mkbootimg_dummy(boot_img_hdr*);
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
new file mode 100644
index 0000000..9ee7869
--- /dev/null
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#define BOOT_MAGIC "ANDROID!"
+#define BOOT_MAGIC_SIZE 8
+#define BOOT_NAME_SIZE 16
+#define BOOT_ARGS_SIZE 512
+#define BOOT_EXTRA_ARGS_SIZE 1024
+
+// The bootloader expects the structure of boot_img_hdr with header
+// version 0 to be as follows:
+struct boot_img_hdr_v0 {
+    // Must be BOOT_MAGIC.
+    uint8_t magic[BOOT_MAGIC_SIZE];
+
+    uint32_t kernel_size; /* size in bytes */
+    uint32_t kernel_addr; /* physical load addr */
+
+    uint32_t ramdisk_size; /* size in bytes */
+    uint32_t ramdisk_addr; /* physical load addr */
+
+    uint32_t second_size; /* size in bytes */
+    uint32_t second_addr; /* physical load addr */
+
+    uint32_t tags_addr; /* physical addr for kernel tags */
+    uint32_t page_size; /* flash page size we assume */
+
+    // Version of the boot image header.
+    uint32_t header_version;
+
+    // Operating system version and security patch level.
+    // For version "A.B.C" and patch level "Y-M-D":
+    //   (7 bits for each of A, B, C; 7 bits for (Y-2000), 4 bits for M)
+    //   os_version = A[31:25] B[24:18] C[17:11] (Y-2000)[10:4] M[3:0]
+    uint32_t os_version;
+
+#if __cplusplus
+    void SetOsVersion(unsigned major, unsigned minor, unsigned patch) {
+        os_version &= ((1 << 11) - 1);
+        os_version |= (((major & 0x7f) << 25) | ((minor & 0x7f) << 18) | ((patch & 0x7f) << 11));
+    }
+
+    void SetOsPatchLevel(unsigned year, unsigned month) {
+        os_version &= ~((1 << 11) - 1);
+        os_version |= (((year - 2000) & 0x7f) << 4) | ((month & 0xf) << 0);
+    }
+#endif
+
+    uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
+
+    uint8_t cmdline[BOOT_ARGS_SIZE];
+
+    uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
+
+    // Supplemental command line data; kept here to maintain
+    // binary compatibility with older versions of mkbootimg.
+    uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];
+} __attribute__((packed));
+
+/*
+ * It is expected that callers would explicitly specify which version of the
+ * boot image header they need to use.
+ */
+typedef struct boot_img_hdr_v0 boot_img_hdr;
+
+/* When a boot header is of version 0, the structure of boot image is as
+ * follows:
+ *
+ * +-----------------+
+ * | boot header     | 1 page
+ * +-----------------+
+ * | kernel          | n pages
+ * +-----------------+
+ * | ramdisk         | m pages
+ * +-----------------+
+ * | second stage    | o pages
+ * +-----------------+
+ *
+ * n = (kernel_size + page_size - 1) / page_size
+ * m = (ramdisk_size + page_size - 1) / page_size
+ * o = (second_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel and ramdisk are required (size != 0)
+ * 2. second is optional (second_size == 0 -> no second)
+ * 3. load each element (kernel, ramdisk, second) at
+ *    the specified physical address (kernel_addr, etc)
+ * 4. prepare tags at tag_addr.  kernel_args[] is
+ *    appended to the kernel commandline in the tags.
+ * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 6. if second_size != 0: jump to second_addr
+ *    else: jump to kernel_addr
+ */
+
+struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
+    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO/ACPIO image */
+    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
+    uint32_t header_size;
+} __attribute__((packed));
+
+/* When the boot image header has a version of 2, 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/acpio | p pages
+ * +---------------------+
+ * | dtb                 | q 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
+ * q = (dtb_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel, ramdisk and DTB are required (size != 0)
+ * 2. recovery_dtbo/recovery_acpio 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, dtb) at
+ *    the specified physical address (kernel_addr, etc)
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery
+ *    dtbo/acpio 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
+ */
+struct boot_img_hdr_v2 : public boot_img_hdr_v1 {
+    uint32_t dtb_size; /* size in bytes for DTB image */
+    uint64_t dtb_addr; /* physical load address for DTB image */
+} __attribute__((packed));
diff --git a/mkbootimg/mkbootimg.py b/mkbootimg/mkbootimg.py
new file mode 100644
index 0000000..92b11a5
--- /dev/null
+++ b/mkbootimg/mkbootimg.py
@@ -0,0 +1,232 @@
+#!/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_IMAGE_HEADER_V1_SIZE = 1648
+    BOOT_IMAGE_HEADER_V2_SIZE = 1660
+    BOOT_MAGIC = 'ANDROID!'.encode()
+
+    if (args.header_version > 2):
+        raise ValueError('Boot header version %d not supported' % args.header_version)
+
+    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)
+    if args.header_version > 1:
+        update_sha(sha, args.dtb)
+
+    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
+
+    # Populate boot image header size for header versions 1 and 2.
+    if args.header_version == 1:
+        args.output.write(pack('I', BOOT_IMAGE_HEADER_V1_SIZE))
+    elif args.header_version == 2:
+        args.output.write(pack('I', BOOT_IMAGE_HEADER_V2_SIZE))
+
+    if args.header_version > 1:
+        args.output.write(pack('I', filesize(args.dtb)))   # size in bytes
+        args.output.write(pack('Q', args.base + args.dtb_offset)) # dtb physical load address
+    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('--dtb', help='path to dtb', type=FileType('rb'))
+    recovery_dtbo_group = parser.add_mutually_exclusive_group()
+    recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+    recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
+                                     type=FileType('rb'), metavar='RECOVERY_ACPIO', dest='recovery_dtbo')
+    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('--dtb_offset', help='dtb offset', type=parse_int, default=0x01f00000)
+
+    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)
+    if args.header_version > 1:
+        write_padded_file(args.output, args.dtb, 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/libmodprobe/libmodprobe_test.h b/mkbootimg/mkbootimg_dummy.cpp
similarity index 70%
copy from libmodprobe/libmodprobe_test.h
copy to mkbootimg/mkbootimg_dummy.cpp
index a001b69..410d379 100644
--- a/libmodprobe/libmodprobe_test.h
+++ b/mkbootimg/mkbootimg_dummy.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#pragma once
+#include <abi_check/mkbootimg_abi_check.h>
 
-#include <string>
-#include <vector>
-
-extern std::vector<std::string> test_modules;
-extern std::vector<std::string> modules_loaded;
+void mkbootimg_dummy(boot_img_hdr* hdr) {
+    // TODO: Hack to trigger abi checks, remove this.
+    if (hdr) {
+        hdr--;
+    }
+}
diff --git a/mkbootimg/unpack_bootimg.py b/mkbootimg/unpack_bootimg.py
new file mode 100755
index 0000000..789bf5e
--- /dev/null
+++ b/mkbootimg/unpack_bootimg.py
@@ -0,0 +1,152 @@
+#!/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, dtb 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: %#x' % kernel_ramdisk_second_info[1])
+    print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
+    print('ramdisk load address: %#x' % kernel_ramdisk_second_info[3])
+    print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
+    print('second bootloader load address: %#x' % kernel_ramdisk_second_info[5])
+    print('kernel tags load address: %#x' % 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: %#x' % 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
+    if version > 1:
+        dtb_size = unpack('I', args.boot_img.read(4))[0]
+        print('dtb size: %s' % dtb_size)
+        dtb_load_address = unpack('Q', args.boot_img.read(8))[0]
+        print('dtb address: %#x' % dtb_load_address)
+    else:
+        dtb_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'))
+
+    if second_size > 0:
+        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'))
+    if dtb_size > 0:
+        num_second_pages = get_number_of_pages(second_size, page_size)
+        num_recovery_dtbo_pages = get_number_of_pages(recovery_dtbo_size, page_size)
+        dtb_offset = page_size * (
+            num_header_pages + num_kernel_pages + num_ramdisk_pages + num_second_pages +
+            num_recovery_dtbo_pages
+        )
+
+        image_info_list.append((dtb_offset, dtb_size, 'dtb'))
+
+    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, recovery dtbo and dtb')
+    parser.add_argument(
+        '--boot_img',
+        help='path to boot image',
+        type=FileType('rb'),
+        required=True)
+    parser.add_argument('--out', help='path to out binaries', default='out')
+    return parser.parse_args()
+
+
+def main():
+    """parse arguments and unpack boot image"""
+    args = parse_cmdline()
+    create_out_dir(args.out)
+    unpack_bootimage(args)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index ac802b5..5c57d69 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -3,7 +3,6 @@
     host_supported: true,
     vendor_available: true,
     recovery_available: true,
-    native_bridge_supported: true,
     srcs: ["property_info_parser.cpp"],
 
     cpp_std: "experimental",
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index aa02a3a..51c1226 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -1,6 +1,7 @@
 cc_defaults {
     name: "propertyinfoserializer_defaults",
     host_supported: true,
+    vendor_available: true,
     cpp_std: "experimental",
     cppflags: [
         "-Wall",
@@ -8,8 +9,8 @@
         "-Werror",
     ],
     static_libs: [
-        "libbase",
         "libpropertyinfoparser",
+        "libbase",
     ],
 }
 
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 33da1f1..6846a69 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -375,9 +375,11 @@
       {"audio_hal.period_size", "u:object_r:default_prop:s0"},
       {"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
       {"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.boot-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
@@ -388,6 +390,7 @@
       {"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+      {"dalvik.vm.image-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},
@@ -585,7 +588,6 @@
       {"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/reboot/Android.bp b/reboot/Android.bp
index cc71723..805fd9a 100644
--- a/reboot/Android.bp
+++ b/reboot/Android.bp
@@ -5,5 +5,4 @@
     srcs: ["reboot.c"],
     shared_libs: ["libcutils"],
     cflags: ["-Werror"],
-    recovery_available: true,
 }
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 19f117f..24b3999 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -58,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)
@@ -88,7 +97,7 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    dev proc sys system data odm oem acct config storage mnt apex debug_ramdisk $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    sbin 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; \
@@ -104,10 +113,10 @@
 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
+ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product_services
 else
-  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/system_ext $(TARGET_ROOT_OUT)/system_ext
+  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product_services $(TARGET_ROOT_OUT)/product_services
 endif
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
@@ -205,7 +214,7 @@
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 
-# Start of i18n and ART APEX compatibility.
+# Start of runtime APEX compatibility.
 #
 # Meta-comment:
 # The placing of this section is somewhat arbitrary. The LOCAL_POST_INSTALL_CMD
@@ -217,29 +226,42 @@
 # come to rely on them.
 
 # http://b/121248172 - create a link from /system/usr/icu to
-# /apex/com.android.i18n/etc/icu so that apps can find the ICU .dat file.
+# /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.i18n/etc/icu $(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 dex2oat
+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.art/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
+    && ln -sf /apex/com.android.runtime/bin/$(b) $(TARGET_OUT)/bin/$(b)) \
 )
 
-# End of i18n and ART APEX compatibilty.
+# End of runtime APEX compatibilty.
 
 ifeq ($(_enforce_vndk_at_runtime),true)
 
 # for VNDK enforced devices
-# This file will be replaced with dynamically generated one from system/linkerconfig
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
-LOCAL_SRC_FILES := etc/ld.config.txt
-include $(BUILD_PREBUILT)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+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)
 
@@ -260,6 +282,36 @@
 
 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
 #
@@ -294,28 +346,36 @@
 include $(BUILD_PREBUILT)
 
 #######################################
-# sanitizer.libraries.txt
+# llndk.libraries.txt
 include $(CLEAR_VARS)
-LOCAL_MODULE := sanitizer.libraries.txt
+LOCAL_MODULE := llndk.libraries.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(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))
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
-	$(hide) $(foreach lib,$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES), \
-		echo $(lib) >> $@;)
+	$(hide) $(foreach lib,$(PRIVATE_LLNDK_LIBRARIES), \
+		echo $(lib).so >> $@;)
+
+#######################################
+# vndksp.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndksp.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
+		echo $(lib).so >> $@;)
 
 #######################################
 # adb_debug.prop in debug ramdisk
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
deleted file mode 100644
index 85d2786..0000000
--- a/rootdir/avb/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-filegroup {
-    name: "q-gsi_avbpubkey",
-    srcs: [
-        "q-gsi.avbpubkey",
-    ],
-}
-
-filegroup {
-    name: "r-gsi_avbpubkey",
-    srcs: [
-        "r-gsi.avbpubkey",
-    ],
-}
-
-filegroup {
-    name: "s-gsi_avbpubkey",
-    srcs: [
-        "s-gsi.avbpubkey",
-    ],
-}
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 5dc019c..f96ffdd 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -16,6 +16,21 @@
 include $(BUILD_PREBUILT)
 
 #######################################
+# q-developer-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := q-developer-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)
 
@@ -31,6 +46,21 @@
 include $(BUILD_PREBUILT)
 
 #######################################
+# r-developer-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := r-developer-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)
 
@@ -44,3 +74,18 @@
 endif
 
 include $(BUILD_PREBUILT)
+
+#######################################
+# s-developer-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := s-developer-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-developer-gsi.avbpubkey b/rootdir/avb/q-developer-gsi.avbpubkey
new file mode 100644
index 0000000..0ace69d
--- /dev/null
+++ b/rootdir/avb/q-developer-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/r-developer-gsi.avbpubkey b/rootdir/avb/r-developer-gsi.avbpubkey
new file mode 100644
index 0000000..aac39cc
--- /dev/null
+++ b/rootdir/avb/r-developer-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/s-developer-gsi.avbpubkey b/rootdir/avb/s-developer-gsi.avbpubkey
new file mode 100644
index 0000000..f0a6c11
--- /dev/null
+++ b/rootdir/avb/s-developer-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index a99756a..5aac61b 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -20,9 +20,6 @@
 
 [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}
@@ -42,58 +39,52 @@
 # APEX related namespaces.
 ###############################################################################
 
-additional.namespaces = art,conscrypt,media,neuralnetworks,resolv
+additional.namespaces = runtime,conscrypt,media,resolv
 
-# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 # 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 art namespace. And then, if the
-# shared library cannot be loaded from the art namespace either, the
+# 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 = art,resolv,neuralnetworks
-namespace.default.asan.links = art,resolv,neuralnetworks
-namespace.default.link.art.shared_libs  = libandroidicu.so
-namespace.default.link.art.shared_libs += libdexfile_external.so
-namespace.default.link.art.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.art.shared_libs += libicui18n.so
-namespace.default.link.art.shared_libs += libicuuc.so
-namespace.default.link.art.shared_libs += libnativebridge.so
-namespace.default.link.art.shared_libs += libnativehelper.so
-namespace.default.link.art.shared_libs += libnativeloader.so
+namespace.default.links = runtime,resolv
+namespace.default.asan.links = runtime,resolv
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
+namespace.default.link.runtime.shared_libs  = libdexfile_external.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
+namespace.default.link.runtime.shared_libs += libandroidicu.so
+# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+namespace.default.link.runtime.shared_libs += libicui18n.so
+namespace.default.link.runtime.shared_libs += libicuuc.so
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.art.shared_libs += libpac.so
+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
 
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
 ###############################################################################
-# "art" APEX namespace
+# "runtime" APEX namespace
 #
-# This namespace exposes externally accessible libraries from the ART APEX.
-# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the Runtime APEX.
 ###############################################################################
-namespace.art.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.art.visible = true
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
 
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = default,neuralnetworks
-# 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.art.link.default.allow_all_shared_libs = true
-namespace.art.link.neuralnetworks.shared_libs = libneuralnetworks.so
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
 # "media" APEX namespace
@@ -129,15 +120,15 @@
 # "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
 
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = art,default
-namespace.conscrypt.link.art.shared_libs = libandroidio.so
+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
@@ -163,28 +154,6 @@
 namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
-# "neuralnetworks" APEX namespace
-#
-# This namespace is for libraries within the NNAPI APEX.
-###############################################################################
-namespace.neuralnetworks.isolated = true
-namespace.neuralnetworks.visible = true
-
-namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs  = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.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 and /product/lib in the search paths. This is because linker
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 5c87843..c95f60f 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -1,3 +1,698 @@
-# This file is no longer in use.
-# Please update linker configuration generator instead.
-# You can find the code from /system/linkerconfig
\ No newline at end of file
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Bionic loader config file.
+#
+
+# Don't change the order here. The first pattern that matches with the
+# absolute path of an executable is selected.
+dir.system = /system/bin/
+dir.system = /system/xbin/
+dir.system = /%PRODUCT%/bin/
+
+dir.vendor = /odm/bin/
+dir.vendor = /vendor/bin/
+dir.vendor = /data/nativetest/odm
+dir.vendor = /data/nativetest64/odm
+dir.vendor = /data/benchmarktest/odm
+dir.vendor = /data/benchmarktest64/odm
+dir.vendor = /data/nativetest/vendor
+dir.vendor = /data/nativetest64/vendor
+dir.vendor = /data/benchmarktest/vendor
+dir.vendor = /data/benchmarktest64/vendor
+
+dir.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 = runtime,conscrypt,media,resolv,sphal,vndk,rs
+
+###############################################################################
+# "default" namespace
+#
+# Framework-side code runs in this namespace. Libs from /vendor partition
+# can't be loaded in this namespace.
+###############################################################################
+namespace.default.isolated = true
+
+namespace.default.search.paths  = /system/${LIB}
+namespace.default.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
+# their absolute paths (e.g. dlopen("/system/lib/vndk/libbase.so");).
+# VNDK libs are built with previous versions of Android and thus must not be
+# loaded into this namespace where libs built with the current version of
+# Android are loaded. Mixing the two types of libs in the same namespace can
+# cause unexpected problem.
+namespace.default.permitted.paths  = /system/${LIB}/drm
+namespace.default.permitted.paths += /system/${LIB}/extractors
+namespace.default.permitted.paths += /system/${LIB}/hw
+namespace.default.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.permitted.paths += /%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
+namespace.default.permitted.paths += /system/priv-app
+namespace.default.permitted.paths += /vendor/framework
+namespace.default.permitted.paths += /vendor/app
+namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /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
+namespace.default.permitted.paths += /oem/app
+namespace.default.permitted.paths += /%PRODUCT%/framework
+namespace.default.permitted.paths += /%PRODUCT%/app
+namespace.default.permitted.paths += /%PRODUCT%/priv-app
+namespace.default.permitted.paths += /%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_SERVICES%/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
+
+namespace.default.asan.permitted.paths  = /data
+namespace.default.asan.permitted.paths += /system/${LIB}/drm
+namespace.default.asan.permitted.paths += /system/${LIB}/extractors
+namespace.default.asan.permitted.paths += /system/${LIB}/hw
+namespace.default.asan.permitted.paths += /system/framework
+namespace.default.asan.permitted.paths += /system/app
+namespace.default.asan.permitted.paths += /system/priv-app
+namespace.default.asan.permitted.paths += /vendor/framework
+namespace.default.asan.permitted.paths += /vendor/app
+namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /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
+namespace.default.asan.permitted.paths += /oem/app
+namespace.default.asan.permitted.paths += /%PRODUCT%/${LIB}
+namespace.default.asan.permitted.paths += /%PRODUCT%/framework
+namespace.default.asan.permitted.paths += /%PRODUCT%/app
+namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
+namespace.default.asan.permitted.paths += /%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 ld.config.txt in the com.android.runtime APEX.
+# 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
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+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
+namespace.default.link.runtime.shared_libs += libandroidicu.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.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+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.asan.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.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+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
+#
+# SP-HAL(Sameprocess-HAL)s are the only vendor libraries that are allowed to be
+# loaded inside system processes. libEGL_<chipset>.so, libGLESv2_<chipset>.so,
+# android.hardware.graphics.mapper@2.0-impl.so, etc are SP-HALs.
+#
+# This namespace is exclusivly for SP-HALs. When the framework tries to dynami-
+# cally load SP-HALs, android_dlopen_ext() is used to explicitly specifying
+# that they should be searched and loaded from this namespace.
+#
+# Note that there is no link from the default namespace to this namespace.
+###############################################################################
+namespace.sphal.isolated = true
+namespace.sphal.visible = true
+
+namespace.sphal.search.paths  = /odm/${LIB}
+namespace.sphal.search.paths += /vendor/${LIB}
+namespace.sphal.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}
+namespace.sphal.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.search.paths +=           /vendor/${LIB}
+
+namespace.sphal.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.sphal.asan.permitted.paths +=           /odm/${LIB}
+namespace.sphal.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.sphal.asan.permitted.paths +=           /vendor/${LIB}
+
+# Once in this namespace, access to libraries in /system/lib is restricted. Only
+# libs listed here can be used. 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%
+
+###############################################################################
+# "rs" namespace
+#
+# This namespace is exclusively for Renderscript internal libraries.
+# This namespace has slightly looser restriction than the vndk namespace because
+# of the genuine characteristics of Renderscript; /data is in the permitted path
+# to load the compiled *.so file and libmediandk.so can be used here.
+###############################################################################
+namespace.rs.isolated = true
+namespace.rs.visible = true
+
+namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
+namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
+namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.search.paths += /odm/${LIB}
+namespace.rs.search.paths += /vendor/${LIB}
+
+namespace.rs.permitted.paths  = /odm/${LIB}
+namespace.rs.permitted.paths += /vendor/${LIB}
+namespace.rs.permitted.paths += /system/vendor/${LIB}
+namespace.rs.permitted.paths += /data
+
+namespace.rs.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
+namespace.rs.asan.search.paths +=           /odm/${LIB}
+namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.search.paths +=           /vendor/${LIB}
+
+namespace.rs.asan.permitted.paths  = /data/asan/odm/${LIB}
+namespace.rs.asan.permitted.paths +=           /odm/${LIB}
+namespace.rs.asan.permitted.paths += /data/asan/vendor/${LIB}
+namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
+namespace.rs.asan.permitted.paths += /data
+
+namespace.rs.links = default,vndk
+
+namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
+namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+# Private LLNDK libs (e.g. libft2.so) are exceptionally allowed to this
+# namespace because RS framework libs are using them.
+namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
+
+namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
+
+###############################################################################
+# "vndk" namespace
+#
+# This namespace is exclusively for vndk-sp libs.
+###############################################################################
+namespace.vndk.isolated = true
+namespace.vndk.visible = true
+
+namespace.vndk.search.paths  = /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+
+namespace.vndk.permitted.paths  = /odm/${LIB}/hw
+namespace.vndk.permitted.paths += /odm/${LIB}/egl
+namespace.vndk.permitted.paths += /vendor/${LIB}/hw
+namespace.vndk.permitted.paths += /vendor/${LIB}/egl
+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
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+
+namespace.vndk.asan.permitted.paths  = /data/asan/odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /odm/${LIB}/egl
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
+namespace.vndk.asan.permitted.paths +=           /vendor/${LIB}/egl
+
+namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
+namespace.vndk.asan.permitted.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%/hw
+
+# The "vndk" namespace links to "default" namespace for LLNDK libs and links to
+# "sphal" namespace for vendor libs.  The ordering matters.  The "default"
+# namespace has higher priority than the "sphal" namespace.
+namespace.vndk.links = default,sphal,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
+# Android releases.
+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
+# the default namespace. 'system' namespace will be added to give limited
+# (LL-NDK only) access.
+###############################################################################
+[vendor]
+additional.namespaces = runtime,system,vndk%VNDK_IN_SYSTEM_NS%
+
+###############################################################################
+# "default" namespace
+#
+# This is the default linker namespace for a vendor process (a process started
+# from /vendor/bin/*). The main executable and the libs under /vendor/lib[64]
+# are loaded directly into this namespace. However, other libs under the system
+# partition (VNDK and LLNDK libraries) are not loaded here but from the
+# separate namespace 'system'. The delegation to the system namespace is done
+# via the 'namespace.default.link.system.shared_libs' property below.
+#
+# '#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
+
+namespace.default.search.paths  = /odm/${LIB}
+namespace.default.search.paths += /vendor/${LIB}
+
+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%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.
+###############################################################################
+namespace.runtime.isolated = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.system.allow_all_shared_libs = true
+
+###############################################################################
+# "vndk" namespace
+#
+# This namespace is where VNDK and VNDK-SP libraries are loaded for
+# a vendor process.
+###############################################################################
+namespace.vndk.isolated = false
+
+namespace.vndk.search.paths  = /odm/${LIB}/vndk
+namespace.vndk.search.paths += /odm/${LIB}/vndk-sp
+namespace.vndk.search.paths += /vendor/${LIB}/vndk
+namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.search.paths += /system/${LIB}/vndk%VNDK_VER%
+
+namespace.vndk.asan.search.paths  = /data/asan/odm/${LIB}/vndk
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk
+namespace.vndk.asan.search.paths += /data/asan/odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /odm/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk
+namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths +=           /vendor/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.vndk.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
+
+# When these NDK libs are required inside this namespace, then it is redirected
+# to the system namespace. This is possible since their ABI is stable across
+# Android releases.  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
+#
+# This namespace is where system libs (VNDK and LLNDK libs) are loaded for
+# a vendor process.
+###############################################################################
+namespace.system.isolated = false
+
+namespace.system.search.paths  = /system/${LIB}
+namespace.system.search.paths += /%PRODUCT%/${LIB}
+namespace.system.search.paths += /%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 += /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
+# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+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
+
+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 ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime,resolv
+namespace.default.visible = true
+
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+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
+namespace.default.link.runtime.shared_libs += libandroidicu.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.
+###############################################################################
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+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.asan.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.
+###############################################################################
+namespace.conscrypt.isolated = true
+namespace.conscrypt.visible = true
+
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+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 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 9c9f4a9..5642559 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,7 +7,6 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
-dir.system = /%SYSTEM_EXT%/bin/
 dir.system = /%PRODUCT%/bin/
 
 dir.vendor = /odm/bin/
@@ -35,7 +34,7 @@
 dir.system = /data
 
 [system]
-additional.namespaces = art,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -44,76 +43,69 @@
 # 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 += /%SYSTEM_EXT%/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /odm/${LIB}
 namespace.default.search.paths += /vendor/${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}
-namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
-namespace.default.asan.search.paths +=           /%SYSTEM_EXT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
-namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
 namespace.default.asan.search.paths += /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
 namespace.default.asan.search.paths += /data/asan/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_SERVICES%/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
-# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
+# Keep in sync with the platform namespace in the com.android.runtime 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 art namespace. And then, if the
-# shared library cannot be loaded from the art namespace either, the
+# 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 = art,resolv,neuralnetworks
-namespace.default.link.art.shared_libs  = libandroidicu.so
-namespace.default.link.art.shared_libs += libdexfile_external.so
-namespace.default.link.art.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.art.shared_libs += libicui18n.so
-namespace.default.link.art.shared_libs += libicuuc.so
-namespace.default.link.art.shared_libs += libnativebridge.so
-namespace.default.link.art.shared_libs += libnativehelper.so
-namespace.default.link.art.shared_libs += libnativeloader.so
+namespace.default.links = runtime,resolv
+# Visible because some libraries are dlopen'ed, e.g. libopenjdk is dlopen'ed by
+# libart.
+namespace.default.visible = true
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+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
+namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.art.shared_libs += libpac.so
+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
 
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
 ###############################################################################
-# "art" APEX namespace
+# "runtime" APEX namespace
 #
-# This namespace pulls in externally accessible libs from the ART APEX.
-# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
+# This namespace pulls in externally accessible libs from the Runtime APEX.
 ###############################################################################
-namespace.art.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.art.visible = true
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
 
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = default,neuralnetworks
-# 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.art.link.default.allow_all_shared_libs = true
-namespace.art.link.neuralnetworks.shared_libs = libneuralnetworks.so
+# Keep in sync with the default namespace in the com.android.runtime APEX
+# ld.config.txt.
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
 # "media" APEX namespace
@@ -128,33 +120,29 @@
 
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
-namespace.media.links = default,neuralnetworks
+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%
 
-# LLNDK library moved into apex
-namespace.media.link.neuralnetworks.shared_libs = libneuralnetworks.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
 
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = art,default
-namespace.conscrypt.link.art.shared_libs = libandroidio.so
+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
-namespace.conscrypt.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -174,7 +162,6 @@
 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.resolv.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 ###############################################################################
 # "sphal" namespace
@@ -190,8 +177,6 @@
 # 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}
@@ -216,7 +201,7 @@
 # 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,neuralnetworks
+namespace.sphal.links = rs,default,vndk
 
 # Renderscript gets separate namespace
 namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -226,9 +211,6 @@
 
 namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
-# LLNDK library moved into apex
-namespace.sphal.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
 ###############################################################################
 # "rs" namespace
 #
@@ -268,7 +250,7 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,neuralnetworks
+namespace.rs.links = default,vndk
 
 namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -276,8 +258,7 @@
 # namespace because RS framework libs are using them.
 namespace.rs.link.default.shared_libs += %PRIVATE_LLNDK_LIBRARIES%
 
-# LLNDK library moved into apex
-namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
+namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
 ###############################################################################
 # "vndk" namespace
@@ -285,8 +266,6 @@
 # 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
@@ -323,33 +302,11 @@
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = default,neuralnetworks
+namespace.vndk.links = default
 
 namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-namespace.vndk.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
-###############################################################################
-# "neuralnetworks" APEX namespace
-#
-# This namespace is for libraries within the NNAPI APEX.
-###############################################################################
-namespace.neuralnetworks.isolated = true
-namespace.neuralnetworks.visible = true
-
-namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs  = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 ###############################################################################
 # Namespace config for vendor processes. In O, no restriction is enforced for
@@ -358,7 +315,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = art,neuralnetworks
+additional.namespaces = runtime
 
 namespace.default.isolated = false
 
@@ -372,9 +329,8 @@
 # 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 += /%SYSTEM_EXT%/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-# Put /system/lib/vndk at the last search order in vndk_lite for GSI
+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}
@@ -393,64 +349,38 @@
 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/%SYSTEM_EXT%/${LIB}
-namespace.default.asan.search.paths +=           /%SYSTEM_EXT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%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.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
 namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
 
-namespace.default.links = art,neuralnetworks
-namespace.default.link.art.shared_libs  = libdexfile_external.so
-namespace.default.link.art.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.art.shared_libs += libicui18n.so
-namespace.default.link.art.shared_libs += libicuuc.so
-namespace.default.link.art.shared_libs += libnativebridge.so
-namespace.default.link.art.shared_libs += libnativehelper.so
-namespace.default.link.art.shared_libs += libnativeloader.so
+namespace.default.links = runtime
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+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.art.shared_libs += libandroidicu.so
-
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 ###############################################################################
-# "art" APEX namespace
+# "runtime" APEX namespace
 #
-# This namespace exposes externally accessible libraries from the ART APEX.
-# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the Runtime APEX.
 ###############################################################################
-namespace.art.isolated = true
+namespace.runtime.isolated = true
 
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = default
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
-namespace.art.link.default.allow_all_shared_libs = true
-
-###############################################################################
-# "neuralnetworks" APEX namespace
-#
-# This namespace is for libraries within the NNAPI APEX.
-###############################################################################
-namespace.neuralnetworks.isolated = true
-namespace.neuralnetworks.visible = true
-
-namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs  = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
+namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
 # Namespace config for native tests that need access to both system and vendor
@@ -459,11 +389,7 @@
 # includes the requisite namespace setup for APEXes.
 ###############################################################################
 [unrestricted]
-additional.namespaces = art,media,conscrypt,resolv,neuralnetworks
-
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.default.visible = true
+additional.namespaces = runtime,media,conscrypt,resolv
 
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /odm/${LIB}
@@ -476,42 +402,39 @@
 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 = art,resolv,neuralnetworks
-namespace.default.link.art.shared_libs  = libandroidicu.so
-namespace.default.link.art.shared_libs += libdexfile_external.so
-namespace.default.link.art.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.art.shared_libs += libicui18n.so
-namespace.default.link.art.shared_libs += libicuuc.so
-namespace.default.link.art.shared_libs += libnativebridge.so
-namespace.default.link.art.shared_libs += libnativehelper.so
-namespace.default.link.art.shared_libs += libnativeloader.so
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+namespace.default.links = runtime,resolv
+namespace.default.visible = true
+
+namespace.default.link.runtime.shared_libs  = libdexfile_external.so
+# libicuuc.so and libicui18n.so are kept for app compat reason. http://b/130788466
+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
+namespace.default.link.runtime.shared_libs += libandroidicu.so
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.art.shared_libs += libpac.so
+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
 
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
 ###############################################################################
-# "art" APEX namespace
+# "runtime" APEX namespace
 #
-# This namespace exposes externally accessible libraries from the ART APEX.
-# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the Runtime APEX.
 ###############################################################################
-namespace.art.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.art.visible = true
+namespace.runtime.isolated = true
+namespace.runtime.visible = true
 
-namespace.art.search.paths = /apex/com.android.art/${LIB}
-namespace.art.asan.search.paths = /apex/com.android.art/${LIB}
-namespace.art.links = default
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
+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/119867084): Restrict to Bionic dlopen dependencies and PALette library
+# when it exists.
 namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
@@ -527,32 +450,28 @@
 
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
-namespace.media.links = default,neuralnetworks
+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%
 
-# LLNDK library moved into apex
-namespace.media.link.neuralnetworks.shared_libs = libneuralnetworks.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
 
+# Keep in sync with ld.config.txt in the com.android.runtime APEX.
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = art,default
-namespace.conscrypt.link.art.shared_libs = libandroidio.so
+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 += %SANITIZER_RUNTIME_LIBRARIES%
 
 ###############################################################################
 # "resolv" APEX namespace
@@ -570,29 +489,6 @@
 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 += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# "neuralnetworks" APEX namespace
-#
-# This namespace is for libraries within the NNAPI APEX.
-###############################################################################
-namespace.neuralnetworks.isolated = true
-namespace.neuralnetworks.visible = true
-
-namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
-namespace.neuralnetworks.links = default
-namespace.neuralnetworks.link.default.shared_libs  = libc.so
-namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.default.shared_libs += libdl.so
-namespace.neuralnetworks.link.default.shared_libs += liblog.so
-namespace.neuralnetworks.link.default.shared_libs += libm.so
-namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
-namespace.neuralnetworks.link.default.shared_libs += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-namespace.neuralnetworks.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
 
 ###############################################################################
 # Namespace config for binaries under /postinstall.
@@ -605,5 +501,5 @@
 [postinstall]
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /%SYSTEM_EXT%/${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 405f5a9..27e855f 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -17,7 +17,7 @@
 libmediandk.so
 libm.so
 libnativewindow.so
-libneuralnetworks.so nopreload
+libneuralnetworks.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
new file mode 100644
index 0000000..29e4519
--- /dev/null
+++ b/rootdir/fsverity_init.sh
@@ -0,0 +1,29 @@
+#!/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
+
+# 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"
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 50005d9..455c9a8 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -5,8 +5,7 @@
     export ANDROID_ASSETS /system/app
     export ANDROID_DATA /data
     export ANDROID_STORAGE /storage
-    export ANDROID_ART_ROOT /apex/com.android.art
-    export ANDROID_I18N_ROOT /apex/com.android.i18n
+    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
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 0827247..e898d4d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -32,19 +32,6 @@
     # 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
-
-    # Create directory to keep ld.config.txt
-    mkdir /dev/linkerconfig 0755
-
-    # Generate ld.config.txt for early executed processes
-    exec -- /system/bin/linkerconfig --target /dev/linkerconfig/ld.config.txt
-    chmod 444 /dev/linkerconfig/ld.config.txt
-
     start ueventd
 
     # Run apexd-bootstrap so that APEXes that provide critical libraries
@@ -52,40 +39,6 @@
     # the libraries are available to the processes started after this statement.
     exec_start apexd-bootstrap
 
-    # These must already exist by the time boringssl_self_test32 / boringssl_self_test64 run.
-    mkdir /dev/boringssl 0755 root root
-    mkdir /dev/boringssl/selftest 0755 root root
-
-# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
-on early-init && property:ro.product.cpu.abilist32=*
-    exec_start boringssl_self_test32
-on early-init && property:ro.product.cpu.abilist64=*
-    exec_start boringssl_self_test64
-on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
-    exec_start boringssl_self_test_apex32
-on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
-    exec_start boringssl_self_test_apex64
-
-service boringssl_self_test32 /system/bin/boringssl_self_test32
-    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test64 /system/bin/boringssl_self_test64
-    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
-    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
-    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
 on init
     sysclktz 0
 
@@ -155,17 +108,6 @@
     mkdir /mnt/media_rw 0750 root media_rw
     mkdir /mnt/user 0755 root root
     mkdir /mnt/user/0 0755 root root
-    mkdir /mnt/user/0/self 0755 root root
-    mkdir /mnt/user/0/emulated 0755 root root
-    mkdir /mnt/user/0/emulated/0 0755 root root
-
-    # Prepare directories for pass through processes
-    mkdir /mnt/pass_through 0755 root root
-    mkdir /mnt/pass_through/0 0755 root root
-    mkdir /mnt/pass_through/0/self 0755 root root
-    mkdir /mnt/pass_through/0/emulated 0755 root root
-    mkdir /mnt/pass_through/0/emulated/0 0755 root root
-
     mkdir /mnt/expand 0771 system system
     mkdir /mnt/appfuse 0711 root root
 
@@ -325,6 +267,12 @@
 
     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"
@@ -340,15 +288,8 @@
     chown system system /sys/power/wakeup_count
     chmod 0660 /sys/power/state
 
-    chown radio wakelock /sys/power/wake_lock
-    chown radio wakelock /sys/power/wake_unlock
-    chmod 0660 /sys/power/wake_lock
-    chmod 0660 /sys/power/wake_unlock
-
     # Start logd before any other services run to ensure we capture all of their logs.
     start logd
-    # Start lmkd before any other services run so that it can register them
-    start lmkd
 
     # Start essential services.
     start servicemanager
@@ -414,6 +355,9 @@
     # Once everything is setup, no need to modify /.
     # The bind+remount combination allows this to work in containers.
     mount rootfs rootfs / remount bind ro nodev
+    # Mount default storage into root namespace
+    mount none /mnt/runtime/default /storage bind rec
+    mount none none /storage slave rec
 
     # Make sure /sys/kernel/debug (if present) is labeled properly
     # Note that tracefs may be mounted under debug, so we need to cross filesystems
@@ -458,8 +402,6 @@
     mkdir /metadata/vold
     chmod 0700 /metadata/vold
     mkdir /metadata/password_slots 0771 root system
-    mkdir /metadata/ota 0700 root system
-    mkdir /metadata/ota/snapshots 0700 root system
 
     mkdir /metadata/apex 0700 root system
     mkdir /metadata/apex/sessions 0700 root system
@@ -475,6 +417,7 @@
     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
@@ -488,26 +431,29 @@
 
     # Start bootcharting as soon as possible after the data partition is
     # mounted to collect more data.
-    mkdir /data/bootchart 0755 shell shell encryption=Require
+    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 encryption=None
+    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 encryption=None
+    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
 
     # create basic filesystem structure
-    mkdir /data/misc 01771 system misc encryption=Require
+    mkdir /data/misc 01771 system misc
     mkdir /data/misc/recovery 0770 system log
     copy /data/misc/recovery/ro.build.fingerprint /data/misc/recovery/ro.build.fingerprint.1
     chmod 0440 /data/misc/recovery/ro.build.fingerprint.1
@@ -548,9 +494,10 @@
     mkdir /data/misc/ethernet 0770 system system
     mkdir /data/misc/dhcp 0770 dhcp dhcp
     mkdir /data/misc/user 0771 root root
+    mkdir /data/misc/perfprofd 0775 root root
     # give system access to wpa_supplicant.conf for backup and restore
     chmod 0660 /data/misc/wifi/wpa_supplicant.conf
-    mkdir /data/local 0751 root root encryption=Require
+    mkdir /data/local 0751 root root
     mkdir /data/misc/media 0700 media media
     mkdir /data/misc/audioserver 0700 audioserver audioserver
     mkdir /data/misc/cameraserver 0700 cameraserver cameraserver
@@ -567,94 +514,88 @@
     mkdir /data/misc/profiles/ref 0771 system system
     mkdir /data/misc/profman 0770 system shell
     mkdir /data/misc/gcov 0770 root root
-    mkdir /data/misc/installd 0700 root root
 
-    mkdir /data/preloads 0775 system system encryption=None
+    mkdir /data/preloads 0775 system system
 
-    mkdir /data/vendor 0771 root root encryption=Require
-    mkdir /data/vendor_ce 0771 root root encryption=None
-    mkdir /data/vendor_de 0771 root root encryption=None
+    mkdir /data/vendor 0771 root root
+    mkdir /data/vendor_ce 0771 root root
+    mkdir /data/vendor_de 0771 root root
     mkdir /data/vendor/hardware 0771 root root
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
     mkdir /data/local/tmp 0771 shell shell
     mkdir /data/local/traces 0777 shell shell
-    mkdir /data/data 0771 system system encryption=None
-    mkdir /data/app-private 0771 system system encryption=Require
-    mkdir /data/app-ephemeral 0771 system system encryption=Require
-    mkdir /data/app-asec 0700 root root encryption=Require
-    mkdir /data/app-lib 0771 system system encryption=Require
-    mkdir /data/app 0771 system system encryption=Require
-    mkdir /data/property 0700 root root encryption=Require
-    mkdir /data/tombstones 0771 system system encryption=Require
+    mkdir /data/data 0771 system system
+    mkdir /data/app-private 0771 system system
+    mkdir /data/app-ephemeral 0771 system system
+    mkdir /data/app-asec 0700 root root
+    mkdir /data/app-lib 0771 system system
+    mkdir /data/app 0771 system system
+    mkdir /data/property 0700 root root
+    mkdir /data/tombstones 0771 system system
     mkdir /data/vendor/tombstones 0771 root root
     mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
 
     # create dalvik-cache, so as to enforce our permissions
-    mkdir /data/dalvik-cache 0771 root root encryption=Require
+    mkdir /data/dalvik-cache 0771 root root
     # create the A/B OTA directory, so as to enforce our permissions
-    mkdir /data/ota 0771 root root encryption=Require
+    mkdir /data/ota 0771 root root
 
     # create the OTA package directory. It will be accessed by GmsCore (cache
     # group), update_engine and update_verifier.
-    mkdir /data/ota_package 0770 system cache encryption=Require
+    mkdir /data/ota_package 0770 system cache
 
     # create resource-cache and double-check the perms
-    mkdir /data/resource-cache 0771 system system encryption=Require
+    mkdir /data/resource-cache 0771 system system
     chown system system /data/resource-cache
     chmod 0771 /data/resource-cache
 
     # create the lost+found directories, so as to enforce our permissions
-    mkdir /data/lost+found 0770 root root encryption=None
+    mkdir /data/lost+found 0770 root root
 
     # create directory for DRM plug-ins - give drm the read/write access to
     # the following directory.
-    mkdir /data/drm 0770 drm drm encryption=Require
+    mkdir /data/drm 0770 drm drm
 
     # create directory for MediaDrm plug-ins - give drm the read/write access to
     # the following directory.
-    mkdir /data/mediadrm 0770 mediadrm mediadrm encryption=Require
+    mkdir /data/mediadrm 0770 mediadrm mediadrm
 
-    mkdir /data/anr 0775 system system encryption=Require
+    mkdir /data/anr 0775 system system
 
     # NFC: create data/nfc for nv storage
-    mkdir /data/nfc 0770 nfc nfc encryption=Require
+    mkdir /data/nfc 0770 nfc nfc
     mkdir /data/nfc/param 0770 nfc nfc
 
     # Create all remaining /data root dirs so that they are made through init
     # and get proper encryption policy installed
-    mkdir /data/backup 0700 system system encryption=Require
-    mkdir /data/ss 0700 system system encryption=Require
+    mkdir /data/backup 0700 system system
+    mkdir /data/ss 0700 system system
 
-    mkdir /data/system 0775 system system encryption=Require
+    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
 
-    mkdir /data/system_de 0770 system system encryption=None
-    mkdir /data/system_ce 0770 system system encryption=None
+    mkdir /data/system_de 0770 system system
+    mkdir /data/system_ce 0770 system system
 
-    mkdir /data/misc_de 01771 system misc encryption=None
-    mkdir /data/misc_ce 01771 system misc encryption=None
+    mkdir /data/misc_de 01771 system misc
+    mkdir /data/misc_ce 01771 system misc
 
-    mkdir /data/user 0711 system system encryption=None
-    mkdir /data/user_de 0711 system system encryption=None
+    mkdir /data/user 0711 system system
+    mkdir /data/user_de 0711 system system
     symlink /data/data /data/user/0
 
-    # Special-case /data/media/obb per b/64566063
-    mkdir /data/media 0770 media_rw media_rw encryption=None
-    mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
+    mkdir /data/media 0770 media_rw media_rw
+    mkdir /data/media/obb 0770 media_rw media_rw
 
-    mkdir /data/cache 0770 system cache encryption=Require
+    mkdir /data/cache 0770 system cache
     mkdir /data/cache/recovery 0770 system cache
     mkdir /data/cache/backup_stage 0700 system system
     mkdir /data/cache/backup 0700 system system
 
-    # Delete these if need be, per b/139193659
-    mkdir /data/rollback 0700 system system encryption=DeleteIfNecessary
-    mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
-
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
     parse_apex_configs
@@ -664,9 +605,8 @@
     # 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 the time zone data
-    # module, delete if not.
-    exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
+    # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
+    exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/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
@@ -683,24 +623,6 @@
     chown root system /dev/fscklogs/log
     chmod 0770 /dev/fscklogs/log
 
-# Switch between sdcardfs and FUSE depending on persist property
-# TODO: Move this to ro property before launch because FDE devices
-# interact with persistent properties differently during boot
-on zygote-start && property:persist.sys.fuse=true
-  # Mount default storage into root namespace
-  mount none /mnt/user/0 /storage bind rec
-  mount none none /storage slave rec
-  # Bootstrap the emulated volume for the pass_through directory for user 0
-  mount none /data/media /mnt/pass_through/0/emulated bind rec
-on zygote-start && property:persist.sys.fuse=false
-  # Mount default storage into root namespace
-  mount none /mnt/runtime/default /storage bind rec
-  mount none none /storage slave rec
-on zygote-start && property:persist.sys.fuse=""
-  # Mount default storage into root namespace
-  mount none /mnt/runtime/default /storage bind rec
-  mount none none /storage slave rec
-
 # 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
@@ -760,8 +682,22 @@
     write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
     write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
 
+    # limit discard size to 128MB in order to avoid long IO latency
+    # for filesystem tuning first (dm or sda)
+    # Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
+    write /sys/devices/virtual/block/${dev.mnt.blk.data}/queue/discard_max_bytes 134217728
+
     # Permissions for System Server and daemons.
+    chown radio system /sys/android_power/state
+    chown radio system /sys/android_power/request_state
+    chown radio system /sys/android_power/acquire_full_wake_lock
+    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 radio wakelock /sys/power/wake_lock
+    chown radio wakelock /sys/power/wake_unlock
+    chmod 0660 /sys/power/wake_lock
+    chmod 0660 /sys/power/wake_unlock
 
     chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate
     chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_rate
@@ -824,9 +760,6 @@
 
     class_start core
 
-    # Requires keystore (currently a core service) to be ready first.
-    exec -- /system/bin/fsverity_init
-
 on nonencrypted
     class_start main
     class_start late_start
@@ -871,7 +804,7 @@
     bootchart stop
     # Setup per_boot directory so other .rc could start to use it on boot_completed
     exec - system system -- /bin/rm -rf /data/per_boot
-    mkdir /data/per_boot 0700 system system encryption=Require key=per_boot_ref
+    mkdir /data/per_boot 0700 system system
 
 # system server cannot write to /proc/sys files,
 # and chown/chmod does not work for /proc/sys/ entries.
@@ -919,29 +852,8 @@
     chmod 0773 /data/misc/trace
     # Give reads to anyone for the window trace folder on debug builds.
     chmod 0775 /data/misc/wmtrace
-
-on init && property:ro.debuggable=1
     start console
 
-on userspace-reboot-requested
-  # TODO(b/135984674): reset all necessary properties here.
-  setprop sys.init.userspace_reboot_in_progress 1
-  setprop sys.boot_completed 0
-  setprop sys.init.updatable_crashing 0
-  setprop apexd.status ""
-
-on userspace-reboot-fs-remount
-  # Make sure that vold is running.
-  # This is mostly a precaution measure in case vold for some reason wasn't running when
-  # userspace reboot was initiated.
-  start vold
-  exec - system system -- /system/bin/vdc checkpoint resetCheckpoint
-  exec - system system -- /system/bin/vdc checkpoint markBootAttempt
-  remount_userdata
-
-on userspace-reboot-resume
-  trigger userspace-reboot-fs-remount
-  trigger post-fs-data
-  trigger zygote-start
-  trigger early-boot
-  trigger boot
+service flash_recovery /system/bin/install-recovery.sh
+    class main
+    oneshot
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index a1888fc..b6cba90 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -9,14 +9,13 @@
     chown system system /sys/class/android_usb/android0/f_rndis/ethaddr
     chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr
     mkdir /data/misc/adb 02750 system shell
-    mkdir /data/adb 0700 root root encryption=Require
+    mkdir /data/adb 0700 root root
 
 # adbd is controlled via property triggers in init.<platform>.usb.rc
 service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
     class core
     socket adbd seqpacket 660 system system
     disabled
-    updatable
     seclabel u:r:adbd:s0
 
 on boot
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 9adbcba..bf3fb42 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,6 +5,7 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket usap_pool_primary stream 660 root system
+    onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
     onrestart restart cameraserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index f6149c9..1bab588 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,6 +5,7 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket usap_pool_primary stream 660 root system
+    onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
     onrestart restart cameraserver
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 0e69b16..6fa210a 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,6 +5,7 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket usap_pool_primary stream 660 root system
+    onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
     onrestart restart cameraserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 3e80168..48461ec 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,6 +5,7 @@
     group root readproc reserved_disk
     socket zygote stream 660 root system
     socket usap_pool_primary stream 660 root system
+    onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart audioserver
     onrestart restart cameraserver
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 9c2cdf2..451f5ad 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -33,7 +33,7 @@
 /dev/urandom              0666   root       root
 # Make HW RNG readable by group system to let EntropyMixer read it.
 /dev/hw_random            0440   root       system
-/dev/ashmem*              0666   root       root
+/dev/ashmem               0666   root       root
 /dev/binder               0666   root       root
 /dev/hwbinder             0666   root       root
 /dev/vndbinder            0666   root       root
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 44f7b65..1282868 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -38,9 +38,8 @@
 
 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.$(vndk_version).txt
-vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.$(vndk_version).txt
-llndk_moved_to_apex_libraries_file := $(library_lists_dir)/llndkinapex.libraries.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
@@ -66,34 +65,19 @@
   vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)
 endif
 
-# LLNDK libraries that has been moved to an apex package and no longer are present on
-# /system image.
-llndk_libraries_moved_to_apex_list:=$(LLNDK_MOVED_TO_APEX_LIBRARIES)
-
-# Returns the unique installed basenames of a module, or module.so if there are
-# none.  The guess is to handle cases like libc, where the module itself is
-# marked uninstallable but a symlink is installed with the name libc.so.
 # $(1): list of libraries
-# $(2): suffix to to add to each library (not used for guess)
-define module-installed-files-or-guess
-$(foreach lib,$(1),$(or $(strip $(sort $(notdir $(call module-installed-files,$(lib)$(2))))),$(lib).so))
-endef
-
-# $(1): list of libraries
-# $(2): suffix to add to each library
-# $(3): output file to write the list of libraries to
+# $(2): output file to write the list of libraries to
 define write-libs-to-file
-$(3): PRIVATE_LIBRARIES := $(1)
-$(3): PRIVATE_SUFFIX := $(2)
-$(3):
-	echo -n > $$@ && $$(foreach so,$$(call module-installed-files-or-guess,$$(PRIVATE_LIBRARIES),$$(PRIVATE_SUFFIX)),echo $$(so) >> $$@;)
+$(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),.vendor,$(vndksp_libraries_file)))
-$(eval $(call write-libs-to-file,$(VNDK_CORE_LIBRARIES),.vendor,$(vndkcore_libraries_file)))
-$(eval $(call write-libs-to-file,$(VNDK_PRIVATE_LIBRARIES),.vendor,$(vndkprivate_libraries_file)))
+$(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)))
+$(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)
 
@@ -107,14 +91,6 @@
   while read -r privatelib; do sed -i.bak "s/$$privatelib//" $(2) ; done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
   sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(2) && \
   rm -f $(2).bak
-
-# # Given a file with a list of libs in "a:b:c" format, filter-out the LLNDK libraries migrated into apex file
-# # and write resulting list to a new file in "a:b:c" format
- $(LOCAL_BUILT_MODULE): private-filter-out-llndk-in-apex-libs = \
-   for lib in $(PRIVATE_LLNDK_LIBRARIES_MOVED_TO_APEX_LIST); do sed -i.bak s/$$lib.so// $(1); done && \
-   sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(1) && \
-   rm -f $(1).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)
@@ -124,11 +100,10 @@
 $(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)\#
-$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_MOVED_TO_APEX_LIST := $(llndk_libraries_moved_to_apex_list)
 deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
   $(vndkprivate_libraries_file)
 ifeq ($(check_backward_compatibility),true)
-deps += $(compatibility_check_script) $(wildcard prebuilts/vndk/*/*/configs/ld.config.*.txt)
+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)
@@ -143,7 +118,6 @@
 endif
 	@mkdir -p $(dir $@)
 	$(call private-filter-out-private-libs,$(PRIVATE_LLNDK_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
-	$(call private-filter-out-llndk-in-apex-libs,$(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" $@
@@ -174,7 +148,12 @@
 	$(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" $@
-	$(hide) sed -i.bak -e "s?%SYSTEM_EXT%?$(TARGET_COPY_OUT_SYSTEM_EXT)?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
@@ -188,7 +167,6 @@
 intermediates_dir :=
 library_lists_dir :=
 llndk_libraries_file :=
-llndk_moved_to_apex_libraries_file :=
 vndksp_libraries_file :=
 vndkcore_libraries_file :=
 vndkprivate_libraries_file :=
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 622de5b..2b35819 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -315,8 +315,7 @@
         PLOG(ERROR) << "setting RLIMIT_NOFILE failed";
     }
 
-    while ((fs_read_atomic_int("/data/misc/installd/layout_version", &fs_version) == -1) ||
-           (fs_version < 3)) {
+    while ((fs_read_atomic_int("/data/.layout_version", &fs_version) == -1) || (fs_version < 3)) {
         LOG(ERROR) << "installd fs upgrade not yet complete; waiting...";
         sleep(1);
     }
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
deleted file mode 100644
index cd8c8c5..0000000
--- a/set-verity-state/Android.bp
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2019 The Android Open Source Project
-
-cc_binary {
-    name: "set-verity-state",
-    srcs: ["set-verity-state.cpp"],
-    shared_libs: [
-        "libbase",
-        "libcrypto",
-        "libcrypto_utils",
-        "libcutils",
-        "libfec",
-        "libfs_mgr",
-        "liblog",
-    ],
-    static_libs: [
-        "libavb_user",
-    ],
-
-    cflags: ["-Werror"],
-    symlinks: [
-        "enable-verity",
-        "disable-verity",
-    ],
-}
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
deleted file mode 100644
index 0a26aba..0000000
--- a/set-verity-state/set-verity-state.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * 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 <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 <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr.h>
-#include <fs_mgr_overlayfs.h>
-#include <fstab/fstab.h>
-#include <log/log_properties.h>
-
-#include "fec/io.h"
-
-#ifdef ALLOW_DISABLE_VERITY
-static const bool kAllowDisableVerity = true;
-#else
-static const bool kAllowDisableVerity = false;
-#endif
-
-using android::base::unique_fd;
-
-static void suggest_run_adb_root() {
-  if (getuid() != 0) printf("Maybe run adb root?\n");
-}
-
-static bool make_block_device_writable(const std::string& dev) {
-  unique_fd fd(open(dev.c_str(), 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(const char* block_device, const char* mount_point,
-                                     bool enable) {
-  if (!make_block_device_writable(block_device)) {
-    printf("Could not make block device %s writable (%s).\n", block_device, strerror(errno));
-    return false;
-  }
-
-  fec::io fh(block_device, O_RDWR);
-
-  if (!fh) {
-    printf("Could not open block device %s (%s).\n", block_device, strerror(errno));
-    suggest_run_adb_root();
-    return false;
-  }
-
-  fec_verity_metadata metadata;
-
-  if (!fh.get_verity_metadata(metadata)) {
-    printf("Couldn't find verity metadata!\n");
-    return false;
-  }
-
-  if (!enable && metadata.disabled) {
-    printf("Verity already disabled on %s\n", mount_point);
-    return false;
-  }
-
-  if (enable && !metadata.disabled) {
-    printf("Verity already enabled on %s\n", mount_point);
-    return false;
-  }
-
-  if (!fh.set_verity_status(enable)) {
-    printf("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) {
-      printf("%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
-    }
-  } else if (errno) {
-    int expected_errno = enable ? EBUSY : ENOENT;
-    if (errno != expected_errno) {
-      printf("Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
-             mount_point, strerror(errno));
-    }
-  }
-  printf("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(bool enable) {
-  auto change = false;
-  errno = 0;
-  if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
-             : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
-    if (change) {
-      printf("%s overlayfs\n", enable ? "disabling" : "using");
-    }
-  } else if (errno) {
-    printf("Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup", strerror(errno));
-    suggest_run_adb_root();
-  }
-  return change;
-}
-
-/* Use AVB to turn verity on/off */
-static bool set_avb_verity_enabled_state(AvbOps* ops, bool enable_verity) {
-  std::string ab_suffix = get_ab_suffix();
-  bool verity_enabled;
-
-  if (is_avb_device_locked()) {
-    printf("Device is locked. Please unlock the device first\n");
-    return false;
-  }
-
-  if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
-    printf("Error getting verity state. Try adb root first?\n");
-    return false;
-  }
-
-  if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
-    printf("verity is already %s\n", verity_enabled ? "enabled" : "disabled");
-    return false;
-  }
-
-  if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
-    printf("Error setting verity\n");
-    return false;
-  }
-
-  overlayfs_setup(enable_verity);
-  printf("Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
-  return true;
-}
-
-int main(int argc, char* argv[]) {
-  if (argc == 0) {
-    LOG(FATAL) << "set-verity-state called with empty argv";
-  }
-
-  std::optional<bool> enable_opt;
-  std::string procname = android::base::Basename(argv[0]);
-  if (procname == "enable-verity") {
-    enable_opt = true;
-  } else if (procname == "disable-verity") {
-    enable_opt = false;
-  }
-
-  if (!enable_opt.has_value()) {
-    if (argc != 2) {
-      printf("usage: %s [1|0]\n", argv[0]);
-      return 1;
-    }
-
-    if (strcmp(argv[1], "1") == 0) {
-      enable_opt = true;
-    } else if (strcmp(argv[1], "0") == 0) {
-      enable_opt = false;
-    } else {
-      printf("usage: %s [1|0]\n", argv[0]);
-      return 1;
-    }
-  }
-
-  bool enable = enable_opt.value();
-
-  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) {
-      printf("%s only works for userdebug builds\n", argv[0]);
-    }
-
-    if (!android::base::GetBoolProperty("ro.secure", false)) {
-      overlayfs_setup(enable);
-      printf("verity not enabled - ENG build\n");
-      return 0;
-    }
-  }
-
-  // Should never be possible to disable dm-verity on a USER build
-  // regardless of using AVB or VB1.0.
-  if (!__android_log_is_debuggable()) {
-    printf("verity cannot be disabled/enabled - USER build\n");
-    return 0;
-  }
-
-  if (using_avb) {
-    // Yep, the system is using AVB.
-    AvbOps* ops = avb_ops_user_new();
-    if (ops == nullptr) {
-      printf("Error getting AVB ops\n");
-      return 1;
-    }
-    if (set_avb_verity_enabled_state(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)) {
-      printf("Failed to read fstab\n");
-      suggest_run_adb_root();
-      return 0;
-    }
-
-    // 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(entry.blk_device.c_str(), entry.mount_point.c_str(), enable)) {
-          any_changed = true;
-        }
-      }
-    }
-  }
-  if (!any_changed) any_changed = overlayfs_setup(enable);
-
-  if (any_changed) {
-    printf("Now reboot your device for settings to take effect\n");
-  }
-
-  return 0;
-}
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index ec4f6ab..3bc3883 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -13,7 +13,7 @@
         "auditctl",
         "awk",
         "bzip2",
-        "ldd",
+        "grep",
         "logwrapper",
         "mini-keyctl",
         "mkshrc",
@@ -25,13 +25,14 @@
         "tcpdump",
         "toolbox",
         "toybox",
-        "ziptool",
+        "unzip",
     ],
 }
 
 phony {
     name: "shell_and_utilities_recovery",
     required: [
+        "grep.recovery",
         "sh.recovery",
         "toolbox.recovery",
         "toybox.recovery",
@@ -43,6 +44,7 @@
     name: "shell_and_utilities_vendor",
     required: [
         "awk_vendor",
+        "grep_vendor",
         "logwrapper_vendor",
         "mkshrc_vendor",
         "sh_vendor",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index d391cc1..ffda3a5 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -211,32 +211,3 @@
 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: 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 cc19481..733b60f 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -24,6 +24,8 @@
         "libbinder",
         "libcutils",
         "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
         "liblog",
         "libprotobuf-cpp-lite",
         "libsysutils",
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 1d934a2..6897663 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -164,10 +164,8 @@
 }
 
 void storaged_t::add_user_ce(userid_t user_id) {
-    if (!proto_loaded[user_id]) {
-        load_proto(user_id);
-        proto_loaded[user_id] = true;
-    }
+    load_proto(user_id);
+    proto_loaded[user_id] = true;
 }
 
 void storaged_t::remove_user_ce(userid_t user_id) {
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 6668cf3..ca2421b 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -41,6 +41,7 @@
 using android::hardware::health::V2_0::StorageInfo;
 
 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
+const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
 const char* emmc_info_t::emmc_ver_str[9] = {
     "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
 };
@@ -61,8 +62,10 @@
     if (healthService != nullptr) {
         return new health_storage_info_t(healthService);
     }
-    if (FileExists(emmc_info_t::emmc_sysfs)) return new emmc_info_t;
-
+    if (FileExists(emmc_info_t::emmc_sysfs) ||
+        FileExists(emmc_info_t::emmc_debugfs)) {
+        return new emmc_info_t;
+    }
     if (FileExists(ufs_info_t::health_file)) {
         return new ufs_info_t;
     }
@@ -238,7 +241,8 @@
 
 void emmc_info_t::report()
 {
-    if (!report_sysfs()) return;
+    if (!report_sysfs() && !report_debugfs())
+        return;
 
     publish();
 }
@@ -280,6 +284,54 @@
     return true;
 }
 
+namespace {
+
+const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
+/* 2 characters in string for each byte */
+const size_t EXT_CSD_REV_IDX = 192 * 2;
+const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
+
+} // namespace
+
+bool emmc_info_t::report_debugfs()
+{
+    string buffer;
+    uint16_t rev = 0;
+
+    if (!ReadFileToString(emmc_debugfs, &buffer) ||
+        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+        return false;
+    }
+
+    string str = buffer.substr(EXT_CSD_REV_IDX, 2);
+    if (!ParseUint(str, &rev) ||
+        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
+        return false;
+    }
+
+    version = "emmc ";
+    version += emmc_ver_str[rev];
+
+    str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
+    if (!ParseUint(str, &eol)) {
+        return false;
+    }
+
+    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
+    if (!ParseUint(str, &lifetime_a)) {
+        return false;
+    }
+
+    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
+    if (!ParseUint(str, &lifetime_b)) {
+        return false;
+    }
+
+    return true;
+}
+
 void ufs_info_t::report()
 {
     string buffer;
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 4ca5f5a..1f852ff 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -1,10 +1,14 @@
 cc_defaults {
     name: "toolbox_defaults",
+
     cflags: [
         "-Werror",
         "-Wno-unused-parameter",
         "-Wno-unused-const-variable",
         "-D_FILE_OFFSET_BITS=64",
+        "-DWITHOUT_NLS",
+        "-DWITHOUT_BZ2",
+        "-DWITHOUT_GZIP",
     ],
 }
 
@@ -24,9 +28,6 @@
         "toolbox.c",
         "getevent.c",
         "getprop.cpp",
-        "modprobe.cpp",
-        "setprop.cpp",
-        "start.cpp",
     ],
     generated_headers: [
         "toolbox_input_labels",
@@ -34,18 +35,11 @@
     shared_libs: [
         "libbase",
     ],
-    static_libs: [
-        "libmodprobe",
-        "libpropertyinfoparser",
-    ],
+    static_libs: ["libpropertyinfoparser"],
 
     symlinks: [
         "getevent",
         "getprop",
-        "modprobe",
-        "setprop",
-        "start",
-        "stop",
     ],
 }
 
@@ -61,3 +55,47 @@
     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"],
+    vendor_available: true,
+}
+
+// We build BSD grep separately (but see http://b/111849261).
+cc_defaults {
+    name: "grep_common",
+    defaults: ["toolbox_defaults"],
+    srcs: [
+        "upstream-netbsd/usr.bin/grep/fastgrep.c",
+        "upstream-netbsd/usr.bin/grep/file.c",
+        "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"],
+    recovery_available: true,
+}
+
+// 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/modprobe.cpp b/toolbox/modprobe.cpp
deleted file mode 100644
index 1b5f54e..0000000
--- a/toolbox/modprobe.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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 <getopt.h>
-#include <stdlib.h>
-#include <iostream>
-
-#include <android-base/strings.h>
-#include <modprobe/modprobe.h>
-
-enum modprobe_mode {
-    AddModulesMode,
-    RemoveModulesMode,
-    ListModulesMode,
-    ShowDependenciesMode,
-};
-
-static void print_usage(void) {
-    std::cerr << "Usage:" << std::endl;
-    std::cerr << std::endl;
-    std::cerr << "  modprobe [-alrqvsDb] [-d DIR] [MODULE]+" << std::endl;
-    std::cerr << "  modprobe [-alrqvsDb] [-d DIR] MODULE [symbol=value][...]" << std::endl;
-    std::cerr << std::endl;
-    std::cerr << "Options:" << std::endl;
-    std::cerr << "  -b: Apply blacklist to module names too" << std::endl;
-    std::cerr << "  -d: Load modules from DIR, option may be used multiple times" << std::endl;
-    std::cerr << "  -D: Print dependencies for modules only, do not load";
-    std::cerr << "  -h: Print this help" << std::endl;
-    std::cerr << "  -l: List modules matching pattern" << std::endl;
-    std::cerr << "  -r: Remove MODULE (multiple modules may be specified)" << std::endl;
-    std::cerr << "  -q: Quiet" << std::endl;
-    std::cerr << "  -v: Verbose" << std::endl;
-    std::cerr << std::endl;
-}
-
-#define check_mode()                                                      \
-    if (mode != AddModulesMode) {                                         \
-        std::cerr << "Error, multiple mode flags specified" << std::endl; \
-        print_usage();                                                    \
-        return EXIT_FAILURE;                                              \
-    }
-
-extern "C" int modprobe_main(int argc, char** argv) {
-    std::vector<std::string> modules;
-    std::string module_parameters;
-    std::vector<std::string> mod_dirs;
-    modprobe_mode mode = AddModulesMode;
-    bool blacklist = false;
-    bool verbose = false;
-    int rv = EXIT_SUCCESS;
-
-    int opt;
-    while ((opt = getopt(argc, argv, "abd:Dhlqrv")) != -1) {
-        switch (opt) {
-            case 'a':
-                // toybox modprobe supported -a to load multiple modules, this
-                // is supported here by default, ignore flag
-                check_mode();
-                break;
-            case 'b':
-                blacklist = true;
-                break;
-            case 'd':
-                mod_dirs.emplace_back(optarg);
-                break;
-            case 'D':
-                check_mode();
-                mode = ShowDependenciesMode;
-                break;
-            case 'h':
-                print_usage();
-                return EXIT_SUCCESS;
-            case 'l':
-                check_mode();
-                mode = ListModulesMode;
-                break;
-            case 'q':
-                verbose = false;
-                break;
-            case 'r':
-                check_mode();
-                mode = RemoveModulesMode;
-                break;
-            case 'v':
-                verbose = true;
-                break;
-            default:
-                std::cerr << "Unrecognized option: " << opt << std::endl;
-                return EXIT_FAILURE;
-        }
-    }
-
-    int parameter_count = 0;
-    for (opt = optind; opt < argc; opt++) {
-        if (!strchr(argv[opt], '=')) {
-            modules.emplace_back(argv[opt]);
-        } else {
-            parameter_count++;
-            if (module_parameters.empty()) {
-                module_parameters = argv[opt];
-            } else {
-                module_parameters = module_parameters + " " + argv[opt];
-            }
-        }
-    }
-
-    if (verbose) {
-        std::cout << "mode is " << mode << std::endl;
-        std::cout << "verbose is " << verbose << std::endl;
-        std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, "") << std::endl;
-        std::cout << "modules is: " << android::base::Join(modules, "") << std::endl;
-        std::cout << "module parameters is: " << android::base::Join(module_parameters, "")
-                  << std::endl;
-    }
-
-    if (modules.empty()) {
-        if (mode == ListModulesMode) {
-            // emulate toybox modprobe list with no pattern (list all)
-            modules.emplace_back("*");
-        } else {
-            std::cerr << "No modules given." << std::endl;
-            print_usage();
-            return EXIT_FAILURE;
-        }
-    }
-    if (mod_dirs.empty()) {
-        std::cerr << "No module configuration directories given." << std::endl;
-        print_usage();
-        return EXIT_FAILURE;
-    }
-    if (parameter_count && modules.size() > 1) {
-        std::cerr << "Only one module may be loaded when specifying module parameters."
-                  << std::endl;
-        print_usage();
-        return EXIT_FAILURE;
-    }
-
-    Modprobe m(mod_dirs);
-    m.EnableVerbose(verbose);
-    if (blacklist) {
-        m.EnableBlacklist(true);
-    }
-
-    for (const auto& module : modules) {
-        switch (mode) {
-            case AddModulesMode:
-                if (!m.LoadWithAliases(module, true, module_parameters)) {
-                    std::cerr << "Failed to load module " << module;
-                    rv = EXIT_FAILURE;
-                }
-                break;
-            case RemoveModulesMode:
-                if (!m.Remove(module)) {
-                    std::cerr << "Failed to remove module " << module;
-                    rv = EXIT_FAILURE;
-                }
-                break;
-            case ListModulesMode: {
-                std::vector<std::string> list = m.ListModules(module);
-                std::cout << android::base::Join(list, "\n") << std::endl;
-                break;
-            }
-            case ShowDependenciesMode: {
-                std::vector<std::string> pre_deps;
-                std::vector<std::string> deps;
-                std::vector<std::string> post_deps;
-                if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
-                    rv = EXIT_FAILURE;
-                    break;
-                }
-                std::cout << "Dependencies for " << module << ":" << std::endl;
-                std::cout << "Soft pre-dependencies:" << std::endl;
-                std::cout << android::base::Join(pre_deps, "\n") << std::endl;
-                std::cout << "Hard dependencies:" << std::endl;
-                std::cout << android::base::Join(deps, "\n") << std::endl;
-                std::cout << "Soft post-dependencies:" << std::endl;
-                std::cout << android::base::Join(post_deps, "\n") << std::endl;
-                break;
-            }
-            default:
-                std::cerr << "Bad mode";
-                rv = EXIT_FAILURE;
-        }
-    }
-
-    return rv;
-}
diff --git a/toolbox/r.c b/toolbox/r.c
new file mode 100644
index 0000000..b96cdb2
--- /dev/null
+++ b/toolbox/r.c
@@ -0,0 +1,102 @@
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#if __LP64__
+#define strtoptr strtoull
+#else
+#define strtoptr strtoul
+#endif
+
+static int usage()
+{
+    fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
+    return -1;
+}
+
+int main(int argc, char *argv[])
+{
+    if(argc < 2) return usage();
+
+    int width = 4;
+    if(!strcmp(argv[1], "-b")) {
+        width = 1;
+        argc--;
+        argv++;
+    } else if(!strcmp(argv[1], "-s")) {
+        width = 2;
+        argc--;
+        argv++;
+    }
+
+    if(argc < 2) return usage();
+    uintptr_t addr = strtoptr(argv[1], 0, 16);
+
+    uintptr_t endaddr = 0;
+    char* end = strchr(argv[1], '-');
+    if (end)
+        endaddr = strtoptr(end + 1, 0, 16);
+
+    if (!endaddr)
+        endaddr = addr + width - 1;
+
+    if (endaddr <= addr) {
+        fprintf(stderr, "end address <= start address\n");
+        return -1;
+    }
+
+    bool set = false;
+    uint32_t value = 0;
+    if(argc > 2) {
+        set = true;
+        value = strtoul(argv[2], 0, 16);
+    }
+
+    int fd = open("/dev/mem", O_RDWR | O_SYNC);
+    if(fd < 0) {
+        fprintf(stderr,"cannot open /dev/mem\n");
+        return -1;
+    }
+
+    off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
+    size_t mmap_size = endaddr - mmap_start + 1;
+    mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+
+    void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
+                        MAP_SHARED, fd, mmap_start);
+
+    if(page == MAP_FAILED){
+        fprintf(stderr,"cannot mmap region\n");
+        return -1;
+    }
+
+    while (addr <= endaddr) {
+        switch(width){
+        case 4: {
+            uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
+            break;
+        }
+        case 2: {
+            uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
+            break;
+        }
+        case 1: {
+            uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
+            if(set) *x = value;
+            fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
+            break;
+        }
+        }
+        addr += width;
+    }
+    return 0;
+}
diff --git a/toolbox/setprop.cpp b/toolbox/setprop.cpp
deleted file mode 100644
index acf8c3e..0000000
--- a/toolbox/setprop.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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
deleted file mode 100644
index b87ed15..0000000
--- a/toolbox/start.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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 bb57e67..abeb3ef 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,7 +1,3 @@
 TOOL(getevent)
 TOOL(getprop)
-TOOL(modprobe)
-TOOL(setprop)
-TOOL(start)
-TOOL(stop)
 TOOL(toolbox)
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
new file mode 100644
index 0000000..2fcd864
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
@@ -0,0 +1,336 @@
+/*	$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
new file mode 100644
index 0000000..428bf58
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/file.c
@@ -0,0 +1,276 @@
+/*	$NetBSD: file.c,v 1.10 2018/08/12 09:03:21 christos 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.10 2018/08/12 09:03:21 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#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>
+
+#include "grep.h"
+
+#define	MAXBUFSIZ	(32 * 1024)
+#define	LNBUFBUMP	80
+
+#ifndef WITHOUT_GZIP
+static gzFile gzbufdesc;
+#endif
+#ifndef WITHOUT_BZ2
+static BZFILE* bzbufdesc;
+#endif
+
+static unsigned char buffer[MAXBUFSIZ + 1];
+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 = -1;
+	int bzerr;
+
+	bufpos = buffer;
+	bufrem = 0;
+
+#ifndef WITHOUT_GZIP
+	if (filebehave == FILE_GZIP) {
+		nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
+		if (nr == -1)
+			return -1;
+	}
+#endif
+#ifndef WITHOUT_BZ2
+	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;
+		}
+		if (nr == -1)
+			return -1;
+	}
+#endif
+	if (nr == -1) {
+		nr = read(f->fd, buffer, MAXBUFSIZ);
+	}
+
+	if (nr < 0)
+		return (-1);
+
+	bufrem = nr;
+	return (0);
+}
+
+static inline void
+grep_lnbufgrow(size_t newlen)
+{
+
+	if (lnbuflen < newlen) {
+		lnbuf = grep_realloc(lnbuf, newlen);
+		lnbuflen = newlen;
+	}
+}
+
+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 */
+		len = p - bufpos;
+		grep_lnbufgrow(len + 1);
+		memcpy(lnbuf, bufpos, len);
+		lnbuf[len] = '\0';
+		*lenp = len;
+		bufrem -= len;
+		bufpos = p;
+		return ((char *)lnbuf);
+	}
+
+	/* 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 */
+		grep_lnbufgrow(len + LNBUFBUMP);
+		memcpy(lnbuf + off, bufpos, len - off);
+		lnbuf[len] = '\0';
+		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;
+		grep_lnbufgrow(len + 1);
+		memcpy(lnbuf + off, bufpos, diff);
+		lnbuf[off + diff] = '\0';
+		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 WITHOUT_GZIP
+	if (filebehave == FILE_GZIP &&
+	    (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
+		goto error;
+#endif
+
+#ifndef WITHOUT_BZ2
+	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
new file mode 100644
index 0000000..bad2a73
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
@@ -0,0 +1,722 @@
+/*	$NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 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.15 2018/08/12 09:03:21 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},
+#ifndef WITHOUT_GZIP
+	{"decompress",          no_argument,            NULL, DECOMPRESS_OPT},
+#endif
+	{"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'},
+#ifndef WITHOUT_BZ2
+	{"bz2decompress",	no_argument,		NULL, 'J'},
+#endif
+	{"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;
+#ifndef WITHOUT_GZIP
+	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;
+#endif
+	}
+
+	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;
+#ifndef WITHOUT_BZ2
+		case 'J':
+			filebehave = FILE_BZIP;
+			break;
+#endif
+		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;
+#ifndef WITHOUT_GZIP
+		case DECOMPRESS_OPT:
+			filebehave = FILE_GZIP;
+			break;
+#endif
+		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) {
+#ifdef _IOLBF
+		setvbuf(stdout, NULL, _IOLBF, 0);
+#else
+		setlinebuf(stdout);
+#endif
+	}
+
+	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
new file mode 100644
index 0000000..b7ef7fa
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
@@ -0,0 +1,162 @@
+/*	$NetBSD: grep.h,v 1.10 2018/08/12 09:03:21 christos 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 WITHOUT_BZ2
+#include <bzlib.h>
+#endif
+#include <limits.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#ifndef WITHOUT_GZIP
+#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
new file mode 100644
index 0000000..e3c6be1
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
@@ -0,0 +1,116 @@
+/*	$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
new file mode 100644
index 0000000..a3c9e4c
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/util.c
@@ -0,0 +1,500 @@
+/*	$NetBSD: util.c,v 1.19 2018/02/05 22:14:26 mrg 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.19 2018/02/05 22:14:26 mrg 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 1fb473e..e807d71 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -2,8 +2,6 @@
 dkrahn@google.com
 drewry@google.com
 gmar@google.com
-mmaurer@google.com
 ncbray@google.com
+rpere@google.com
 swillden@google.com
-trong@google.com
-wenhaowang@google.com
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index e553af1..65b271a 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -1,3 +1,4 @@
+//
 // Copyright (C) 2015 The Android Open-Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,15 +20,14 @@
 // to only building on ARM if they include assembly. Individual makefiles
 // are responsible for having their own logic, for fine-grained control.
 
-cc_binary {
-    name: "android.hardware.gatekeeper@1.0-service.trusty",
-    defaults: ["hidl_defaults"],
+cc_library_shared {
+    name: "gatekeeper.trusty",
     vendor: true,
+
     relative_install_path: "hw",
-    init_rc: ["android.hardware.gatekeeper@1.0-service.trusty.rc"],
 
     srcs: [
-        "service.cpp",
+        "module.cpp",
         "trusty_gatekeeper_ipc.c",
         "trusty_gatekeeper.cpp",
     ],
@@ -39,15 +39,10 @@
     ],
 
     shared_libs: [
-        "android.hardware.gatekeeper@1.0",
-        "libbase",
-        "libhidlbase",
         "libgatekeeper",
-        "libutils",
         "liblog",
         "libcutils",
         "libtrusty",
     ],
-
-    vintf_fragments: ["android.hardware.gatekeeper@1.0-service.trusty.xml"],
+    header_libs: ["libhardware_headers"],
 }
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
deleted file mode 100644
index 5413a6c..0000000
--- a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index 19714a8..0000000
--- a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<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
new file mode 100644
index 0000000..0ee3c2f
--- /dev/null
+++ b/trusty/gatekeeper/module.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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
deleted file mode 100644
index c5ee488..0000000
--- a/trusty/gatekeeper/service.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 d149664..b3fbfa9 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -16,131 +16,147 @@
 
 #define LOG_TAG "TrustyGateKeeper"
 
-#include <android-base/logging.h>
-#include <limits>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <type_traits>
+
+#include <log/log.h>
 
 #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 {
 
-constexpr const uint32_t SEND_BUF_SIZE = 8192;
-constexpr const uint32_t RECV_BUF_SIZE = 8192;
+const uint32_t SEND_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = 8192;
 
-TrustyGateKeeperDevice::TrustyGateKeeperDevice() {
+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;
+
     int rc = trusty_gatekeeper_connect();
     if (rc < 0) {
-        LOG(ERROR) << "Error initializing trusty session: " << rc;
+        ALOGE("Error initializing trusty session: %d", 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();
 }
 
-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::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) {
 
-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) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+        return error_;
     }
 
-    if (desiredPassword.size() == 0) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+    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);
     }
 
-    EnrollRequest request(uid, hidl_vec2sized_buffer(currentPasswordHandle),
-                          hidl_vec2sized_buffer(desiredPassword),
-                          hidl_vec2sized_buffer(currentPassword));
+    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;
-    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});
+
+    gatekeeper_error_t error = Send(request, &response);
+
+    if (error == ERROR_RETRY) {
+        return response.retry_timeout;
+    } else if (error != ERROR_NONE) {
+        return -EINVAL;
     }
-    return {};
+
+    *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
+    *enrolled_password_handle_length = response.enrolled_password_handle.length;
+
+
+    return 0;
 }
 
-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) {
+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) {
     if (error_ != 0) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+        return error_;
     }
 
-    if (enrolledPasswordHandle.size() == 0) {
-        _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);
 
-    VerifyRequest request(uid, challenge, hidl_vec2sized_buffer(enrolledPasswordHandle),
-                          hidl_vec2sized_buffer(providedPassword));
+    VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
     VerifyResponse 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());
+    gatekeeper_error_t error = Send(request, &response);
 
-        _hidl_cb({response.request_reenroll ? GatekeeperStatusCode::STATUS_REENROLL
-                                            : GatekeeperStatusCode::STATUS_OK,
-                  response.retry_timeout, auth_token});
+    if (error == ERROR_RETRY) {
+        return response.retry_timeout;
+    } else if (error != ERROR_NONE) {
+        return -EINVAL;
     }
-    return {};
-}
 
-Return<void> TrustyGateKeeperDevice::deleteUser(uint32_t /*uid*/, deleteUser_cb _hidl_cb) {
-    _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
-    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::deleteAllUsers(deleteAllUsers_cb _hidl_cb) {
-    _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
-    return {};
+    if (request_reenroll != NULL) {
+        *request_reenroll = response.request_reenroll;
+    }
+
+    return 0;
 }
 
 gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
@@ -156,7 +172,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) {
-        LOG(ERROR) << "error (" << rc << ") calling gatekeeper TA";
+        ALOGE("error (%d) calling gatekeeper TA", rc);
         return ERROR_INVALID;
     }
 
@@ -166,4 +182,51 @@
     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 c0713f4..2becc49 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.h
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -17,34 +17,84 @@
 #ifndef TRUSTY_GATEKEEPER_H
 #define TRUSTY_GATEKEEPER_H
 
-#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
-#include <hidl/Status.h>
-
-#include <memory>
-
+#include <hardware/gatekeeper.h>
 #include <gatekeeper/gatekeeper_messages.h>
 
 #include "gatekeeper_ipc.h"
 
 namespace gatekeeper {
 
-class TrustyGateKeeperDevice : public ::android::hardware::gatekeeper::V1_0::IGatekeeper {
-  public:
-    explicit TrustyGateKeeperDevice();
+class TrustyGateKeeperDevice {
+    public:
+
+    explicit TrustyGateKeeperDevice(const hw_module_t* module);
     ~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.
      */
-    ::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;
+    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);
 
     /**
      * Verifies provided_password matches enrolled_password_handle.
@@ -59,32 +109,18 @@
      * Returns: 0 on success or an error code less than 0 on error
      * On error, verification token will not be allocated
      */
-    ::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 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> deleteUser(uint32_t uid, deleteUser_cb _hidl_cb) override;
+    static int close_device(hw_device_t* dev);
 
-    ::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);
-    }
-
+    gatekeeper_device device_;
     int error_;
-};
 
-}  // namespace gatekeeper
+};
+}
 
 #endif
 
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
deleted file mode 100644
index ec2ba12..0000000
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- **
- ** 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 <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#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;
-
-typedef ::android::hardware::keymaster::V3_0::Tag Tag3;
-using ::android::hardware::keymaster::V4_0::Constants;
-
-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);
-}
-
-/*
- * injectAuthToken translates a KM4 authToken into a legacy AUTH_TOKEN tag
- *
- * Currently, system/keymaster's reference implementation only accepts this
- * method for passing an auth token, so until that changes we need to
- * translate to the old format.
- */
-inline hidl_vec<KeyParameter> injectAuthToken(const hidl_vec<KeyParameter>& keyParamsBase,
-                                              const HardwareAuthToken& authToken) {
-    std::vector<KeyParameter> keyParams(keyParamsBase);
-    const size_t mac_len = static_cast<size_t>(Constants::AUTH_TOKEN_MAC_LENGTH);
-    /*
-     * mac.size() == 0 indicates no token provided, so we should not copy.
-     * mac.size() != mac_len means it is incompatible with the old
-     *   hw_auth_token_t structure. This is forbidden by spec, but to be safe
-     *   we only copy if mac.size() == mac_len, e.g. there is an authToken
-     *   with a hw_auth_token_t compatible MAC.
-     */
-    if (authToken.mac.size() == mac_len) {
-        KeyParameter p;
-        p.tag = static_cast<Tag>(Tag3::AUTH_TOKEN);
-        p.blob.resize(sizeof(hw_auth_token_t));
-
-        hw_auth_token_t* auth_token = reinterpret_cast<hw_auth_token_t*>(p.blob.data());
-        auth_token->version = 0;
-        auth_token->challenge = authToken.challenge;
-        auth_token->user_id = authToken.userId;
-        auth_token->authenticator_id = authToken.authenticatorId;
-        auth_token->authenticator_type =
-                htobe32(static_cast<uint32_t>(authToken.authenticatorType));
-        auth_token->timestamp = htobe64(authToken.timestamp);
-        static_assert(mac_len == sizeof(auth_token->hmac));
-        memcpy(auth_token->hmac, authToken.mac.data(), mac_len);
-        keyParams.push_back(p);
-    }
-
-    return hidl_vec<KeyParameter>(std::move(keyParams));
-}
-
-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) {
-    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
-    BeginOperationRequest request;
-    request.purpose = legacy_enum_conversion(purpose);
-    request.SetKeyMaterial(key.data(), key.size());
-    request.additional_params.Reinitialize(KmParamSet(extendedParams));
-
-    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)verificationToken;
-    UpdateOperationRequest request;
-    UpdateOperationResponse response;
-    hidl_vec<KeyParameter> resultParams;
-    hidl_vec<uint8_t> resultBlob;
-    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
-    uint32_t resultConsumed = 0;
-
-    request.op_handle = operationHandle;
-    request.additional_params.Reinitialize(KmParamSet(extendedParams));
-
-    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)verificationToken;
-    FinishOperationRequest request;
-    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
-    request.op_handle = operationHandle;
-    request.input.Reinitialize(input.data(), input.size());
-    request.signature.Reinitialize(signature.data(), signature.size());
-    request.additional_params.Reinitialize(KmParamSet(extendedParams));
-
-    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
deleted file mode 100644
index 72c9167..0000000
--- a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-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/android.hardware.keymaster@4.0-service.trusty.xml b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
deleted file mode 100644
index aa30707..0000000
--- a/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="device">
-    <hal format="hidl">
-        <name>android.hardware.keymaster</name>
-        <transport>hwbinder</transport>
-        <version>4.0</version>
-        <interface>
-        <name>IKeymasterDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-</manifest>
diff --git a/trusty/keymaster/4.0/service.cpp b/trusty/keymaster/4.0/service.cpp
deleted file mode 100644
index 96eb584..0000000
--- a/trusty/keymaster/4.0/service.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-**
-** 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 f32a69e..819851f 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -101,41 +101,10 @@
         "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",
-        "libtrusty",
-        "libkeymaster_messages",
-        "libkeymaster4",
-        "android.hardware.keymaster@4.0"
-    ],
-
-    vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
-}
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index f3ef747..7f5e87f 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -172,25 +172,24 @@
     ForwardCommand(KM_ABORT_OPERATION, request, response);
 }
 
+/* Methods for Keymaster 4.0 functionality -- not yet implemented */
 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);
+    response.error = KM_ERROR_UNIMPLEMENTED;
     return response;
 }
 
 ComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(
-        const ComputeSharedHmacRequest& request) {
+        const ComputeSharedHmacRequest& /* request */) {
     ComputeSharedHmacResponse response;
-    ForwardCommand(KM_COMPUTE_SHARED_HMAC, request, &response);
+    response.error = KM_ERROR_UNIMPLEMENTED;
     return response;
 }
 
 VerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(
-        const VerifyAuthorizationRequest& request) {
+        const VerifyAuthorizationRequest& /* request */) {
     VerifyAuthorizationResponse response;
-    ForwardCommand(KM_VERIFY_AUTHORIZATION, request, &response);
+    response.error = KM_ERROR_UNIMPLEMENTED;
     return response;
 }
 
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h
deleted file mode 100644
index 2be15bc..0000000
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- **
- ** 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/trusty-base.mk b/trusty/trusty-base.mk
index fd8daa8..0a0ecec 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -19,12 +19,9 @@
 # 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 += \
-	android.hardware.keymaster@4.0-service.trusty \
-	android.hardware.gatekeeper@1.0-service.trusty
+	android.hardware.keymaster@3.0-service.trusty \
+	gatekeeper.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
index 9c8af7b..77d1f70 100644
--- a/trusty/utils/trusty-ut-ctrl/Android.bp
+++ b/trusty/utils/trusty-ut-ctrl/Android.bp
@@ -20,8 +20,6 @@
     shared_libs: [
         "libc",
         "liblog",
-    ],
-    static_libs: [
         "libtrusty",
     ],
     gtest: false,
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 6a339a1..3afa7a9 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -5,6 +5,7 @@
     shared_libs: [
         "libbase",
         "libhidlbase",
+        "libhidltransport",
         "liblog",
         "libutils",
         "libhardware",