Merge "avoid data overflow in low memory device"
diff --git a/adb/Android.bp b/adb/Android.bp
index 57872b0..170053b 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -27,7 +27,6 @@
         "-DADB_HOST=1",         // overridden by adbd_defaults
         "-DALLOW_ADBD_ROOT=0",  // overridden by adbd_defaults
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
-        "-DENABLE_FASTDEPLOY=1", // enable fast deploy
     ],
     cpp_std: "experimental",
 
@@ -253,7 +252,7 @@
         "libbase",
         "libcutils",
         "libcrypto_utils",
-        "libcrypto",
+        "libcrypto_static",
         "libdiagnose_usb",
         "liblog",
         "libusb",
@@ -423,14 +422,6 @@
         "liblog",
     ],
 
-    product_variables: {
-        debuggable: {
-            required: [
-                "remount",
-            ],
-        },
-    },
-
     target: {
         android: {
             srcs: [
@@ -438,7 +429,6 @@
                 "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",
             ],
@@ -553,7 +543,7 @@
         "libbase",
         "libbootloader_message",
         "libcap",
-        "libcrypto",
+        "libcrypto_static",
         "libcrypto_utils",
         "libcutils",
         "libdiagnose_usb",
@@ -621,7 +611,7 @@
         "libbootloader_message",
         "libcutils",
         "libcrypto_utils",
-        "libcrypto",
+        "libcrypto_static",
         "libdiagnose_usb",
         "liblog",
         "libusb",
@@ -629,6 +619,7 @@
         "libselinux",
     ],
     test_suites: ["device-tests"],
+    require_root: true,
 }
 
 python_test_host {
@@ -690,6 +681,7 @@
     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",
@@ -726,6 +718,7 @@
     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",
     ],
@@ -753,6 +746,9 @@
         },
     },
     data: [
+        "fastdeploy/testdata/rotating_cube-metadata-release.data",
         "fastdeploy/testdata/rotating_cube-release.apk",
+        "fastdeploy/testdata/sample.apk",
+        "fastdeploy/testdata/sample.cd",
     ],
 }
diff --git a/adb/adb.cpp b/adb/adb.cpp
index ba6df7a..1ec145b 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1131,7 +1131,9 @@
 
     if (service == "features") {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t =
+                s->transport ? s->transport
+                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t != nullptr) {
             SendOkay(reply_fd, FeatureSetToString(t->features()));
         } else {
@@ -1190,7 +1192,9 @@
     // These always report "unknown" rather than the actual error, for scripts.
     if (service == "get-serialno") {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t =
+                s->transport ? s->transport
+                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, !t->serial.empty() ? t->serial : "unknown");
         } else {
@@ -1200,7 +1204,9 @@
     }
     if (service == "get-devpath") {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t =
+                s->transport ? s->transport
+                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, !t->devpath.empty() ? t->devpath : "unknown");
         } else {
@@ -1210,7 +1216,9 @@
     }
     if (service == "get-state") {
         std::string error;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &error);
+        atransport* t =
+                s->transport ? s->transport
+                             : acquire_one_transport(type, serial, transport_id, nullptr, &error);
         if (t) {
             SendOkay(reply_fd, t->connection_state_name());
         } else {
@@ -1234,7 +1242,9 @@
 
     if (service == "reconnect") {
         std::string response;
-        atransport* t = acquire_one_transport(type, serial, transport_id, nullptr, &response, true);
+        atransport* t = s->transport ? s->transport
+                                     : acquire_one_transport(type, serial, transport_id, nullptr,
+                                                             &response, true);
         if (t != nullptr) {
             kick_transport(t, true);
             response =
@@ -1246,8 +1256,15 @@
 
     // TODO: Switch handle_forward_request to string_view.
     std::string service_str(service);
-    if (handle_forward_request(
-                service_str.c_str(), [=](std::string* error) { return s->transport; }, reply_fd)) {
+    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)) {
         return HostRequestResult::Handled;
     }
 
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index a6e8998..73dcde1 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -37,9 +37,7 @@
 #include "commandline.h"
 #include "fastdeploy.h"
 
-#if defined(ENABLE_FASTDEPLOY)
 static constexpr int kFastDeployMinApi = 24;
-#endif
 
 namespace {
 
@@ -146,15 +144,7 @@
     *buf = '\0';
 }
 
-#if defined(ENABLE_FASTDEPLOY)
-static int delete_device_patch_file(const char* apkPath) {
-    std::string patchDevicePath = get_patch_path(apkPath);
-    return delete_device_file(patchDevicePath);
-}
-#endif
-
-static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
-                                bool use_localagent) {
+static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
     printf("Performing Streamed Install\n");
 
     // The last argument must be the APK file
@@ -176,98 +166,80 @@
         error_exit("--fastdeploy doesn't support .apex files");
     }
 
-    if (use_fastdeploy == true) {
-#if defined(ENABLE_FASTDEPLOY)
-        TemporaryFile metadataTmpFile;
-        std::string patchTmpFilePath;
-        {
-            TemporaryFile patchTmpFile;
-            patchTmpFile.DoNotRemove();
-            patchTmpFilePath = patchTmpFile.path;
+    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));
         }
+    }
 
-        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
-        extract_metadata(file, metadataFile);
-        fclose(metadataFile);
-
-        create_patch(file, metadataTmpFile.path, patchTmpFilePath.c_str());
-        // pass all but 1st (command) and last (apk path) parameters through to pm for
-        // session creation
-        std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
-        install_patch(file, patchTmpFilePath.c_str(), pm_args.size(), pm_args.data());
-        adb_unlink(patchTmpFilePath.c_str());
-        delete_device_patch_file(file);
-        return 0;
-#else
-        error_exit("fastdeploy is disabled");
-#endif
-    } else {
-        struct stat sb;
-        if (stat(file, &sb) == -1) {
-            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-            return 1;
-        }
-
-        unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
-        if (local_fd < 0) {
-            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-            return 1;
-        }
-
-#ifdef __linux__
-        posix_fadvise(local_fd.get(), 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
-#endif
-
-        const bool use_abb = can_use_feature(kFeatureAbb);
-        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);
-            return 0;
-        }
-        fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+    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;
+    }
+
+#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);
+        return 0;
+    }
+    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+    return 1;
 }
 
-static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
-                              bool use_localagent) {
+static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy) {
     printf("Performing Push Install\n");
 
     // Find last APK argument.
@@ -288,35 +260,26 @@
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
     std::string apk_dest = "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
-
-    if (use_fastdeploy == true) {
-#if defined(ENABLE_FASTDEPLOY)
-        TemporaryFile metadataTmpFile;
-        TemporaryFile patchTmpFile;
-
-        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
-        extract_metadata(apk_file[0], metadataFile);
-        fclose(metadataFile);
-
-        create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
-        apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
-#else
-        error_exit("fastdeploy is disabled");
-#endif
-    } else {
-        if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
-    }
-
     argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
-    result = pm_command(argc, argv);
 
-cleanup_apk:
-    if (use_fastdeploy == true) {
-#if defined(ENABLE_FASTDEPLOY)
-        delete_device_patch_file(apk_file[0]);
-#endif
+    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;
+        }
     }
-    delete_device_file(apk_dest);
+
+    if (do_sync_push(apk_file, apk_dest.c_str(), false)) {
+        result = pm_command(argc, argv);
+        delete_device_file(apk_dest);
+    }
+
     return result;
 }
 
@@ -325,7 +288,6 @@
     InstallMode 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++) {
@@ -354,11 +316,6 @@
         } 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
         }
     }
 
@@ -370,14 +327,13 @@
         error_exit("Attempting to use streaming install on unsupported device");
     }
 
-#if defined(ENABLE_FASTDEPLOY)
-    if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
+    if (use_fastdeploy && get_device_api_level() < kFastDeployMinApi) {
         printf("Fast Deploy is only compatible with devices of API version %d or higher, "
                "ignoring.\n",
                kFastDeployMinApi);
         use_fastdeploy = false;
     }
-#endif
+    fastdeploy_set_agent_update_strategy(agent_update_strategy);
 
     std::vector<const char*> passthrough_argv;
     for (int i = 0; i < argc; i++) {
@@ -390,26 +346,13 @@
         error_exit("install requires an apk argument");
     }
 
-    if (use_fastdeploy == true) {
-#if defined(ENABLE_FASTDEPLOY)
-        fastdeploy_set_local_agent(use_localagent);
-        update_agent(agent_update_strategy);
-
-        // The last argument must be the APK file
-        const char* file = passthrough_argv.back();
-        use_fastdeploy = find_package(file);
-#else
-        error_exit("fastdeploy is disabled");
-#endif
-    }
-
     switch (installMode) {
         case INSTALL_PUSH:
             return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
-                                      use_fastdeploy, use_localagent);
+                                      use_fastdeploy);
         case INSTALL_STREAM:
             return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
-                                        use_fastdeploy, use_localagent);
+                                        use_fastdeploy);
         case INSTALL_DEFAULT:
         default:
             return 1;
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index ed6a9a8..e8be784 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -173,7 +173,8 @@
 
     RSA* key = RSA_new();
     if (!PEM_read_RSAPrivateKey(fp.get(), &key, nullptr, nullptr)) {
-        LOG(ERROR) << "Failed to read key";
+        LOG(ERROR) << "Failed to read key from '" << file << "'";
+        ERR_print_errors_fp(stderr);
         RSA_free(key);
         return nullptr;
     }
@@ -249,7 +250,7 @@
     return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
 }
 
-static bool generate_userkey() {
+static bool load_userkey() {
     std::string path = get_user_key_path();
     if (path.empty()) {
         PLOG(ERROR) << "Error getting user key filename";
@@ -435,8 +436,8 @@
 void adb_auth_init() {
     LOG(INFO) << "adb_auth_init...";
 
-    if (!generate_userkey()) {
-        LOG(ERROR) << "Failed to generate user key";
+    if (!load_userkey()) {
+        LOG(ERROR) << "Failed to load (or generate) user key";
         return;
     }
 
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index fe2bdfe..0ffdbc2 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -255,13 +255,8 @@
 }
 #endif
 
-// Reads from |fd| and prints received data. If |use_shell_protocol| is true
-// this expects that incoming data will use the shell protocol, in which case
-// stdout/stderr are routed independently and the remote exit code will be
-// returned.
-// if |callback| is non-null, stdout/stderr output will be handled by it.
-int read_and_dump(borrowed_fd fd, bool use_shell_protocol = false,
-                  StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
+int read_and_dump(borrowed_fd fd, bool use_shell_protocol,
+                  StandardStreamsCallbackInterface* callback) {
     int exit_code = 0;
     if (fd < 0) return exit_code;
 
@@ -1713,8 +1708,12 @@
         }
 
         if (CanUseFeature(features, kFeatureRemountShell)) {
-            const char* arg[2] = {"shell", "remount"};
-            return adb_shell(2, arg);
+            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:");
         }
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index cd5933a..ab77b29 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -109,6 +109,14 @@
         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) {
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index fbae219..bdc9e56 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -30,112 +30,114 @@
 #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 = 0x00000002;
+static constexpr long kRequiredAgentVersion = 0x00000003;
 
-static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+static constexpr int kPackageMissing = 3;
+static constexpr int kInvalidAgentVersion = 4;
+
 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 {
 
-    if (statusCode == 0 && versionOutputBuffer.size() > 0) {
-        version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
+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()));
+        }
     }
 
-    return version;
-}
+  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
 
 int get_device_api_level() {
-    std::vector<char> sdkVersionOutputBuffer;
-    std::vector<char> sdkVersionErrorBuffer;
+    REPORT_FUNC_TIME();
+    std::vector<char> sdk_version_output_buffer;
+    std::vector<char> sdk_version_error_buffer;
     int api_level = -1;
 
-    int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
-                                           &sdkVersionErrorBuffer);
-    if (statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
-        api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
+    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);
     }
 
     return api_level;
 }
 
-void fastdeploy_set_local_agent(bool use_localagent) {
-    g_use_localagent = use_localagent;
+void fastdeploy_set_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy) {
+    g_agent_update_strategy = agent_update_strategy;
 }
 
-static bool deploy_agent(bool checkTimeStamps) {
+static void push_to_device(const void* data, size_t byte_count, const char* dst, bool sync) {
     std::vector<const char*> srcs;
-    // TODO: Deploy agent from bin2c directly instead of writing to disk first.
-    TemporaryFile tempAgent;
-    android::base::WriteFully(tempAgent.fd, kDeployAgent, sizeof(kDeployAgent));
-    srcs.push_back(tempAgent.path);
-    if (!do_sync_push(srcs, kDeviceAgentFile, checkTimeStamps)) {
+    {
+        TemporaryFile temp;
+        android::base::WriteFully(temp.fd, data, byte_count);
+        srcs.push_back(temp.path);
+
+        // On Windows, the file needs to be flushed before pushing to device.
+        // closing the file flushes its content, but we still need to remove it after push.
+        // FileDeleter does exactly that.
+        temp.DoNotRemove();
+    }
+    FileDeleter temp_deleter(srcs.back());
+
+    if (!do_sync_push(srcs, dst, sync)) {
         error_exit("Failed to push fastdeploy agent to device.");
     }
-    srcs.clear();
-    // TODO: Deploy agent from bin2c directly instead of writing to disk first.
-    TemporaryFile tempAgentScript;
-    android::base::WriteFully(tempAgentScript.fd, kDeployAgentScript, sizeof(kDeployAgentScript));
-    srcs.push_back(tempAgentScript.path);
-    if (!do_sync_push(srcs, kDeviceAgentScript, checkTimeStamps)) {
-        error_exit("Failed to push fastdeploy agent script to device.");
-    }
-    srcs.clear();
+}
+
+static bool deploy_agent(bool check_time_stamps) {
+    REPORT_FUNC_TIME();
+
+    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 chmodCommand =
+    std::string chmod_command =
             android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentScript);
-    int ret = send_shell_command(chmodCommand);
+    int ret = send_shell_command(chmod_command);
     if (ret != 0) {
-        error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
+        error_exit("Error executing %s returncode: %d", chmod_command.c_str(), ret);
     }
 
     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) {
@@ -147,29 +149,29 @@
     return utf8;
 }
 
-static std::string get_packagename_from_apk(const char* apkPath) {
+static std::string get_package_name_from_apk(const char* apk_path) {
 #undef open
-    std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
+    std::unique_ptr<android::ZipFileRO> zip_file((android::ZipFileRO::open)(apk_path));
 #define open ___xxx_unix_open
-    if (zipFile == nullptr) {
-        perror_exit("Could not open %s", apkPath);
+    if (zip_file == nullptr) {
+        perror_exit("Could not open %s", apk_path);
     }
-    android::ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
+    android::ZipEntryRO entry = zip_file->findEntryByName("AndroidManifest.xml");
     if (entry == nullptr) {
-        error_exit("Could not find AndroidManifest.xml inside %s", apkPath);
+        error_exit("Could not find AndroidManifest.xml inside %s", apk_path);
     }
     uint32_t manifest_len = 0;
-    if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
-        error_exit("Could not read AndroidManifest.xml inside %s", apkPath);
+    if (!zip_file->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
+        error_exit("Could not read AndroidManifest.xml inside %s", apk_path);
     }
     std::vector<char> manifest_data(manifest_len);
-    if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
-        error_exit("Could not uncompress AndroidManifest.xml inside %s", apkPath);
+    if (!zip_file->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
+        error_exit("Could not uncompress AndroidManifest.xml inside %s", apk_path);
     }
     android::ResXMLTree tree;
     android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
     if (setto_status != android::OK) {
-        error_exit("Could not parse AndroidManifest.xml inside %s", apkPath);
+        error_exit("Could not parse AndroidManifest.xml inside %s", apk_path);
     }
     android::ResXMLParser::event_code_t code;
     while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
@@ -210,80 +212,97 @@
                 break;
         }
     }
-    error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
+    error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apk_path);
 }
 
-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 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;
+}
 
-    std::vector<char> extractErrorBuffer;
-    DeployAgentFileCallback cb(outputFp, &extractErrorBuffer);
-    int returnCode = send_shell_command(extractCommand, false, &cb);
+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);
+    }
     if (returnCode != 0) {
-        fprintf(stderr, "Executing %s returned %d\n", extractCommand.c_str(), returnCode);
-        fprintf(stderr, "%*s\n", int(extractErrorBuffer.size()), extractErrorBuffer.data());
+        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());
         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);
 }
 
-void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
-    DeployPatchGenerator generator(false);
-    unique_fd patchFd(adb_open(patchPath, O_WRONLY | O_CREAT | O_CLOEXEC));
-    if (patchFd < 0) {
-        perror_exit("adb: failed to create %s", patchPath);
-    }
-    bool success = generator.CreatePatch(apkPath, metadataPath, patchFd);
-    if (!success) {
-        error_exit("Failed to create patch for %s", apkPath);
-    }
-}
+unique_fd install_patch(int argc, const char** argv) {
+    REPORT_FUNC_TIME();
+    constexpr char kAgentApplyServicePattern[] = "shell:/data/local/tmp/deployagent apply - -pm %s";
 
-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::vector<unsigned char> apply_output_buffer;
+    std::vector<unsigned char> apply_error_buffer;
     std::string argsString;
 
     bool rSwitchPresent = false;
@@ -298,17 +317,42 @@
         argsString.append("-r");
     }
 
-    std::string applyPatchCommand =
-            android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
-                                        patchDevicePath.c_str(), argsString.c_str());
-    int returnCode = send_shell_command(applyPatchCommand);
-    if (returnCode != 0) {
-        error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
+    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);
     }
 }
 
-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);
+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());
 }
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
index 7b7f2ec..830aeb2 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -16,6 +16,11 @@
 
 #pragma once
 
+#include "adb_unique_fd.h"
+
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+#include <optional>
 #include <string>
 
 enum FastDeploy_AgentUpdateStrategy {
@@ -24,12 +29,11 @@
     FastDeploy_AgentUpdateDifferentVersion
 };
 
-void fastdeploy_set_local_agent(bool use_localagent);
+void fastdeploy_set_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy);
 int get_device_api_level();
-void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy);
-void extract_metadata(const char* apkPath, FILE* outputFp);
-void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
-void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
-void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
-std::string get_patch_path(const char* apkPath);
-bool find_package(const char* apkPath);
+
+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);
diff --git a/adb/client/fastdeploycallbacks.cpp b/adb/client/fastdeploycallbacks.cpp
index 23a0aca..86ceaa1 100644
--- a/adb/client/fastdeploycallbacks.cpp
+++ b/adb/client/fastdeploycallbacks.cpp
@@ -49,35 +49,7 @@
 int capture_shell_command(const char* command, std::vector<char>* outBuffer,
                           std::vector<char>* errBuffer) {
     DeployAgentBufferCallback cb(outBuffer, errBuffer);
-    return send_shell_command(command, false, &cb);
-}
-
-DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer) {
-    mpOutFile = outputFile;
-    mpErrBuffer = errBuffer;
-    mBytesWritten = 0;
-}
-
-void DeployAgentFileCallback::OnStdout(const char* buffer, int length) {
-    if (mpOutFile != NULL) {
-        int bytes_written = fwrite(buffer, 1, length, mpOutFile);
-        if (bytes_written != length) {
-            printf("Write error %d\n", bytes_written);
-        }
-        mBytesWritten += bytes_written;
-    }
-}
-
-void DeployAgentFileCallback::OnStderr(const char* buffer, int length) {
-    appendBuffer(mpErrBuffer, buffer, length);
-}
-
-int DeployAgentFileCallback::Done(int status) {
-    return status;
-}
-
-int DeployAgentFileCallback::getBytesWritten() {
-    return mBytesWritten;
+    return send_shell_command(command, /*disable_shell_protocol=*/false, &cb);
 }
 
 DeployAgentBufferCallback::DeployAgentBufferCallback(std::vector<char>* outBuffer,
diff --git a/adb/client/fastdeploycallbacks.h b/adb/client/fastdeploycallbacks.h
index 7e049c5..4a6fb99 100644
--- a/adb/client/fastdeploycallbacks.h
+++ b/adb/client/fastdeploycallbacks.h
@@ -17,23 +17,6 @@
 #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 5d10238..703eb2f 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -849,6 +849,16 @@
     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) {
@@ -862,8 +872,8 @@
     std::vector<copyinfo> file_list;
     std::vector<std::string> directory_list;
 
-    for (std::string dirpath = rpath; dirpath != "/"; dirpath = android::base::Dirname(dirpath)) {
-        directory_list.push_back(dirpath);
+    for (std::string path = rpath; !is_root_dir(path); path = android::base::Dirname(path)) {
+        directory_list.push_back(path);
     }
     std::reverse(directory_list.begin(), directory_list.end());
 
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 17b4db1..24e722e 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -406,25 +406,44 @@
     }
 }
 
-int usb_write(usb_handle *h, const void *_data, int len)
-{
+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) {
     D("++ usb_write ++");
 
-    unsigned char *data = (unsigned char*) _data;
+    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.
     int n = usb_bulk_write(h, data, len);
-    if (n != len) {
-        D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
+    if (n == -1 && errno == ENOMEM) {
+        n = usb_write_split(h, data, len);
+    }
+
+    if (n == -1) {
         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 ? n : -1;
+        return usb_bulk_write(h, _data, 0) == 0 ? len : -1;
     }
 
     D("-- usb_write --");
-    return n;
+    return len;
 }
 
 int usb_read(usb_handle *h, void *_data, int len)
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index e5a4917..9ebab74 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -248,6 +248,12 @@
         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/remount_service.cpp b/adb/daemon/remount_service.cpp
deleted file mode 100644
index 6bd7855..0000000
--- a/adb/daemon/remount_service.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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);
-        const char* msg = "failed to exec remount\n";
-        write(STDERR_FILENO, msg, strlen(msg));
-        _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) {
-    do_remount(fd.get(), cmd);
-    // The remount command will print success or failure for us.
-}
diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h
deleted file mode 100644
index 522a5da..0000000
--- a/adb/daemon/remount_service.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-#include "adb_unique_fd.h"
-
-#if defined(__ANDROID__)
-void remount_service(unique_fd, const std::string&);
-#endif
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index e6f4499..181a8c5 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -54,7 +54,6 @@
 #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"
@@ -251,9 +250,9 @@
     if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
     } else if (android::base::ConsumePrefix(&name, "remount:")) {
-        std::string arg(name);
-        return create_service_thread("remount",
-                                     std::bind(remount_service, std::placeholders::_1, arg));
+        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 arg(name);
         return create_service_thread("reboot",
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 95e1a28..f5893aa 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -13,15 +13,77 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-java_binary {
-    name: "deployagent",
+java_library {
+    name: "deployagent_lib",
     sdk_version: "24",
-    srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
-    static_libs: ["apkzlib_zip"],
+    srcs: [
+        "deployagent/src/**/*.java",
+        "proto/**/*.proto",
+    ],
     proto: {
         type: "lite",
     },
+}
+
+java_binary {
+    name: "deployagent",
+    sdk_version: "24",
+    static_libs: [
+        "deployagent_lib",
+    ],
     dex_preopt: {
         enabled: false,
     }
-}
\ No newline at end of file
+}
+
+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",
+    ],
+}
diff --git a/adb/fastdeploy/AndroidManifest.xml b/adb/fastdeploy/AndroidManifest.xml
new file mode 100644
index 0000000..89dc745
--- /dev/null
+++ b/adb/fastdeploy/AndroidManifest.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.
+-->
+
+<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
new file mode 100644
index 0000000..24a72bc
--- /dev/null
+++ b/adb/fastdeploy/AndroidTest.xml
@@ -0,0 +1,40 @@
+<?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/src/com/android/fastdeploy/ApkArchive.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/ApkArchive.java
new file mode 100644
index 0000000..31e0502
--- /dev/null
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/ApkArchive.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 a8103c4..3812307 100644
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -24,18 +24,22 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
-import java.io.OutputStream;
 import java.io.RandomAccessFile;
-import java.util.Set;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.WritableByteChannel;
 
+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 = 0x00000002;
+    private static final int AGENT_VERSION = 0x00000003;
 
     public static void main(String[] args) {
         int exitCode = 0;
@@ -45,68 +49,70 @@
             }
 
             String commandString = args[0];
+            switch (commandString) {
+                case "dump": {
+                    if (args.length != 3) {
+                        showUsage(1);
+                    }
 
-            if (commandString.equals("extract")) {
-                if (args.length != 2) {
-                    showUsage(1);
-                }
-
-                String packageName = args[1];
-                extractMetaData(packageName);
-            } else if (commandString.equals("find")) {
-                if (args.length != 2) {
-                    showUsage(1);
-                }
-
-                String packageName = args[1];
-                if (getFilenameFromPackageName(packageName) == null) {
-                    exitCode = 3;
-                }
-            } else if (commandString.equals("apply")) {
-                if (args.length < 4) {
-                    showUsage(1);
-                }
-
-                String packageName = args[1];
-                String patchPath = args[2];
-                String outputParam = args[3];
-
-                InputStream deltaInputStream = null;
-                if (patchPath.compareTo("-") == 0) {
-                    deltaInputStream = System.in;
-                } else {
-                    deltaInputStream = new FileInputStream(patchPath);
-                }
-
-                if (outputParam.equals("-o")) {
-                    OutputStream outputStream = null;
-                    if (args.length > 4) {
-                        String outputPath = args[4];
-                        if (!outputPath.equals("-")) {
-                            outputStream = new FileOutputStream(outputPath);
+                    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;
                     }
-                    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);
+                    break;
                 }
-            } else if (commandString.equals("version")) {
-                System.out.printf("0x%08X\n", AGENT_VERSION);
-            } else {
-                showUsage(1);
+                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:
+                    showUsage(1);
+                    break;
             }
         } catch (Exception e) {
             System.err.println("Error: " + e);
@@ -118,16 +124,16 @@
 
     private static void showUsage(int exitCode) {
         System.err.println(
-            "usage: deployagent <command> [<args>]\n\n" +
-            "commands:\n" +
-            "version                             get the version\n" +
-            "find PKGNAME                        return zero if package found, else non-zero\n" +
-            "extract PKGNAME                     extract an installed package's metadata\n" +
-            "apply PKGNAME PATCHFILE [-o|-pm]    apply a patch from PATCHFILE (- for stdin) to an installed package\n" +
-            " -o <FILE> directs output to FILE, default or - for stdout\n" +
-            " -pm <ARGS> directs output to package manager, passes <ARGS> to 'pm install-create'\n"
-            );
-
+                "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"
+        );
         System.exit(exitCode);
     }
 
@@ -162,32 +168,34 @@
                 }
                 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 File getFileFromPackageName(String packageName) throws IOException {
-        String filename = getFilenameFromPackageName(packageName);
-        if (filename == null) {
-            // Should not happen (function is only called when we know the package exists)
-            throw new IOException("package not found");
-        }
-        return new File(filename);
-    }
+    private static void dumpApk(String packageName, String packagePath) throws IOException {
+        File apk = new File(packagePath);
+        ApkArchive.Dump dump = new ApkArchive(apk).extractMetadata();
 
-    private static void extractMetaData(String packageName) throws IOException {
-        File apkFile = getFileFromPackageName(packageName);
-        APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
-        apkMetaData.writeTo(System.out);
+        APKDump.Builder apkDumpBuilder = APKDump.newBuilder();
+        apkDumpBuilder.setName(packageName);
+        if (dump.cd != null) {
+            apkDumpBuilder.setCd(ByteString.copyFrom(dump.cd));
+        }
+        if (dump.signature != null) {
+            apkDumpBuilder.setSignature(ByteString.copyFrom(dump.signature));
+        }
+        apkDumpBuilder.setAbsolutePath(apk.getAbsolutePath());
+
+        apkDumpBuilder.build().writeTo(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] + " ");
         }
 
@@ -199,7 +207,8 @@
         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)));
             }
         }
 
@@ -213,16 +222,15 @@
         return p.exitValue();
     }
 
-    private static int applyPatch(String packageName, InputStream deltaStream, String[] sessionArgs)
+    private static int applyPatch(InputStream deltaStream, String[] sessionArgs)
             throws IOException, PatchFormatException {
-        File deviceFile = getFileFromPackageName(packageName);
         int sessionId = createInstallSession(sessionArgs);
         if (sessionId < 0) {
             System.err.println("PM Create Session Failed");
             return -1;
         }
 
-        int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
+        int writeExitCode = writePatchedDataToSession(deltaStream, sessionId);
         if (writeExitCode == 0) {
             return commitInstallSession(sessionId);
         } else {
@@ -230,84 +238,94 @@
         }
     }
 
-    private static long writePatchToStream(RandomAccessFile oldData, InputStream patchData,
-        OutputStream outputStream) throws IOException, PatchFormatException {
+    private static long writePatchToStream(InputStream patchData,
+            OutputStream outputStream) throws IOException, PatchFormatException {
         long newSize = readPatchHeader(patchData);
-        long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, outputStream);
+        long bytesWritten = writePatchedDataToStream(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, 0, signatureBuffer.length);
+            PatchUtils.readFully(patchData, signatureBuffer);
         } catch (IOException e) {
             throw new PatchFormatException("truncated signature");
         }
 
-        String signature = new String(signatureBuffer, 0, signatureBuffer.length, "US-ASCII");
+        String signature = new String(signatureBuffer);
         if (!PatchUtils.SIGNATURE.equals(signature)) {
             throw new PatchFormatException("bad signature");
         }
 
-        long newSize = PatchUtils.readBsdiffLong(patchData);
-        if (newSize < 0 || newSize > Integer.MAX_VALUE) {
-            throw new PatchFormatException("bad newSize");
+        long newSize = PatchUtils.readLELong(patchData);
+        if (newSize < 0) {
+            throw new PatchFormatException("bad newSize: " + newSize);
         }
 
         return newSize;
     }
 
     // Note that this function assumes patchData has been seek'ed to the start of the delta stream
-    // (i.e. the signature has already been read by readPatchHeader). For a stream that points to the
-    // start of a patch file call writePatchToStream
-    private static long writePatchedDataToStream(RandomAccessFile oldData, long newSize,
-        InputStream patchData, OutputStream outputStream) throws IOException {
+    // (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);
+
         long newDataBytesWritten = 0;
         byte[] buffer = new byte[BUFFER_SIZE];
 
         while (newDataBytesWritten < newSize) {
-            long copyLen = PatchUtils.readFormattedLong(patchData);
-            if (copyLen > 0) {
-                PatchUtils.pipe(patchData, outputStream, buffer, (int) copyLen);
+            long newDataLen = PatchUtils.readLELong(patchData);
+            if (newDataLen > 0) {
+                PatchUtils.pipe(patchData, outputStream, buffer, newDataLen);
             }
 
-            long oldDataOffset = PatchUtils.readFormattedLong(patchData);
-            long oldDataLen = PatchUtils.readFormattedLong(patchData);
-            oldData.seek(oldDataOffset);
-            if (oldDataLen > 0) {
-                PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
+            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;
+                }
             }
-            newDataBytesWritten += copyLen + oldDataLen;
+            newDataBytesWritten += newDataLen + oldDataLen;
         }
 
         return newDataBytesWritten;
     }
 
-    private static int writePatchedDataToSession(RandomAccessFile oldData, InputStream patchData, int sessionId)
+    private static int writePatchedDataToSession(InputStream patchData, int sessionId)
             throws IOException, PatchFormatException {
         try {
             Process p;
             long newSize = readPatchHeader(patchData);
-            StringBuilder commandBuilder = new StringBuilder();
-            commandBuilder.append(String.format("pm install-write -S %d %d -- -", newSize, sessionId));
-
-            String command = commandBuilder.toString();
+            String command = String.format("pm install-write -S %d %d -- -", newSize, sessionId);
             p = Runtime.getRuntime().exec(command);
 
             OutputStream sessionOutputStream = p.getOutputStream();
-            long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, sessionOutputStream);
+            long bytesWritten = writePatchedDataToStream(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/deploylib/src/com/android/fastdeploy/PatchFormatException.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchFormatException.java
similarity index 100%
rename from adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
rename to adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchFormatException.java
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java
new file mode 100644
index 0000000..54be26f
--- /dev/null
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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
new file mode 100644
index 0000000..7c2468f
--- /dev/null
+++ b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/ApkArchiveTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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
new file mode 100644
index 0000000..ef6ccae
--- /dev/null
+++ b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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.tradefed.device.DeviceNotAvailableException;
+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 commandResult = getDevice().executeShellCommand("pm list packages");
+        final int prefixLength = "package:".length();
+        return Arrays.stream(commandResult.split("\\r?\\n"))
+                .anyMatch(line -> line.substring(prefixLength).equals(packageName));
+    }
+
+    // Mostly copied from PkgInstallSignatureVerificationTest.java.
+    private String fastInstallPackage(String apkPath)
+            throws IOException, DeviceNotAvailableException {
+        return getDevice().executeAdbCommand("install", "-t", "--fastdeploy", "--force-agent",
+                apkPath);
+    }
+}
+
+
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
deleted file mode 100644
index c60f9a6..0000000
--- a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
+++ /dev/null
@@ -1,186 +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.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 = "FASTDEPLOY";
-
-    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
new file mode 100644
index 0000000..3dc5e50
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
new file mode 100644
index 0000000..7127800
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/apk_archive.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <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
new file mode 100644
index 0000000..554cb57
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/apk_archive_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
index 22c9243..8aa7da7 100644
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
@@ -25,8 +25,12 @@
 #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"
@@ -34,9 +38,6 @@
 using namespace com::android::fastdeploy;
 
 void DeployPatchGenerator::Log(const char* fmt, ...) {
-    if (!is_verbose_) {
-        return;
-    }
     va_list ap;
     va_start(ap, fmt);
     vprintf(fmt, ap);
@@ -44,19 +45,34 @@
     va_end(ap);
 }
 
-void DeployPatchGenerator::APKEntryToLog(const APKEntry& entry) {
-    Log("Filename: %s", entry.filename().c_str());
-    Log("CRC32: 0x%08llX", entry.crc32());
-    Log("Data Offset: %lld", entry.dataoffset());
-    Log("Compressed Size: %lld", entry.compressedsize());
-    Log("Uncompressed Size: %lld", entry.uncompressedsize());
+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::APKMetaDataToLog(const char* file, const APKMetaData& metadata) {
+void DeployPatchGenerator::APKEntryToLog(const APKEntry& entry) {
     if (!is_verbose_) {
         return;
     }
-    Log("APK Metadata: %s", file);
+    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);
@@ -65,49 +81,93 @@
 
 void DeployPatchGenerator::ReportSavings(const std::vector<SimpleEntry>& identicalEntries,
                                          uint64_t totalSize) {
-    long totalEqualBytes = 0;
-    int totalEqualFiles = 0;
+    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->compressedsize();
+            totalEqualBytes += identicalEntries[i].localEntry->datasize();
             totalEqualFiles++;
         }
     }
-    float savingPercent = (totalEqualBytes * 100.0f) / totalSize;
-    fprintf(stderr, "Detected %d equal APK entries\n", totalEqualFiles);
-    fprintf(stderr, "%ld bytes are equal out of %" PRIu64 " (%.2f%%)\n", totalEqualBytes, totalSize,
-            savingPercent);
+    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 char* localApkPath, borrowed_fd output) {
-    unique_fd input(adb_open(localApkPath, O_RDONLY | O_CLOEXEC));
+                                         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.
-    for (auto&& entry : entriesToUseOnDevice) {
-        int64_t deviceDataOffset = entry.deviceEntry->dataoffset();
+    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 deviceDataLength = entry.deviceEntry->compressedsize();
+        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;
-        PatchUtils::WriteLong(deltaFromDeviceDataStart, output);
         if (deltaFromDeviceDataStart > 0) {
-            PatchUtils::Pipe(input, output, deltaFromDeviceDataStart);
+            WritePatchEntry(patchEntry, input, output, &realSizeOut);
+            patchEntry.deltaFromDeviceDataStart = deltaFromDeviceDataStart;
+            patchEntry.deviceDataOffset = deviceDataOffset;
+            patchEntry.deviceDataLength = deviceDataLength;
+        } else {
+            patchEntry.deviceDataLength += deviceDataLength;
         }
-        PatchUtils::WriteLong(deviceDataOffset, output);
-        PatchUtils::WriteLong(deviceDataLength, output);
-        adb_lseek(input, deviceDataLength, SEEK_CUR);
-        currentSizeOut += deltaFromDeviceDataStart + deviceDataLength;
+
+        currentSizeOut += deltaFromDeviceDataStart + hostDataLength;
     }
-    if (currentSizeOut != newApkSize) {
+    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);
@@ -115,44 +175,72 @@
     }
 }
 
-bool DeployPatchGenerator::CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
-                                       borrowed_fd output) {
-    std::string content;
-    APKMetaData deviceApkMetadata;
-    if (android::base::ReadFileToString(deviceApkMetadataPath, &content)) {
-        deviceApkMetadata.ParsePartialFromString(content);
-    } else {
-        // TODO: What do we want to do if we don't find any metadata.
-        // The current fallback behavior is to build a patch with the contents of |localApkPath|.
-    }
+bool DeployPatchGenerator::CreatePatch(const char* localApkPath, APKMetaData deviceApkMetadata,
+                                       android::base::borrowed_fd output) {
+    return CreatePatch(PatchUtils::GetHostAPKMetaData(localApkPath), std::move(deviceApkMetadata),
+                       output);
+}
 
-    APKMetaData localApkMetadata = PatchUtils::GetAPKMetaData(localApkPath);
-    // Log gathered metadata info.
-    APKMetaDataToLog(deviceApkMetadataPath, deviceApkMetadata);
-    APKMetaDataToLog(localApkPath, localApkMetadata);
+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, output);
+    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 (int i = 0; i < localApkMetadata.entries_size(); i++) {
-        const APKEntry& localEntry = localApkMetadata.entries(i);
-        totalSize += localEntry.compressedsize();
-        for (int j = 0; j < deviceApkMetadata.entries_size(); j++) {
-            const APKEntry& deviceEntry = deviceApkMetadata.entries(j);
-            if (deviceEntry.crc32() == localEntry.crc32() &&
-                deviceEntry.filename().compare(localEntry.filename()) == 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 = const_cast<APKEntry*>(&localEntry);
-                simpleEntry.deviceEntry = const_cast<APKEntry*>(&deviceEntry);
+                simpleEntry.localEntry = &localEntry;
+                simpleEntry.deviceEntry = deviceEntry;
                 APKEntryToLog(localEntry);
                 outIdenticalEntries.push_back(simpleEntry);
                 break;
@@ -164,4 +252,4 @@
                   return lhs.localEntry->dataoffset() < rhs.localEntry->dataoffset();
               });
     return totalSize;
-}
\ No newline at end of file
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
index 30e41a5..fd7eaee 100644
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
@@ -27,12 +27,15 @@
  */
 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 {
-        com::android::fastdeploy::APKEntry* localEntry;
-        com::android::fastdeploy::APKEntry* deviceEntry;
+        const APKEntry* localEntry;
+        const APKEntry* deviceEntry;
     };
 
     /**
@@ -41,10 +44,10 @@
      */
     explicit DeployPatchGenerator(bool is_verbose) : is_verbose_(is_verbose) {}
     /**
-     * Given a |localApkPath|, and the |deviceApkMetadataPath| from an installed APK this function
+     * Given a |localApkPath|, and the |deviceApkMetadata| from an installed APK this function
      * writes a patch to the given |output|.
      */
-    bool CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
+    bool CreatePatch(const char* localApkPath, APKMetaData deviceApkMetadata,
                      android::base::borrowed_fd output);
 
   private:
@@ -57,14 +60,20 @@
 
     /**
      * Helper function to log the APKMetaData structure. If |is_verbose_| is false this function
-     * early outs. |file| is the path to the file represented by |metadata|. This function is used
-     * for debugging / information.
+     * early outs. This function is used for debugging / information.
      */
-    void APKMetaDataToLog(const char* file, const com::android::fastdeploy::APKMetaData& metadata);
+    void APKMetaDataToLog(const APKMetaData& metadata);
     /**
      * Helper function to log APKEntry.
      */
-    void APKEntryToLog(const com::android::fastdeploy::APKEntry& entry);
+    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
@@ -92,11 +101,11 @@
      * highest.
      */
     void GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
-                       const char* localApkPath, android::base::borrowed_fd output);
+                       const std::string& localApkPath, const std::string& deviceApkPath,
+                       android::base::borrowed_fd output);
 
   protected:
-    uint64_t BuildIdenticalEntries(
-            std::vector<SimpleEntry>& outIdenticalEntries,
-            const com::android::fastdeploy::APKMetaData& localApkMetadata,
-            const com::android::fastdeploy::APKMetaData& deviceApkMetadataPath);
-};
\ No newline at end of file
+    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
index 9cdc44e..e4c96ea 100644
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "deploy_patch_generator.h"
+#include "apk_archive.h"
 #include "patch_utils.h"
 
 #include <android-base/file.h>
@@ -31,21 +32,17 @@
     return "fastdeploy/testdata/" + name;
 }
 
-class TestPatchGenerator : DeployPatchGenerator {
-  public:
-    TestPatchGenerator() : DeployPatchGenerator(false) {}
-    void GatherIdenticalEntries(std::vector<DeployPatchGenerator::SimpleEntry>& outIdenticalEntries,
-                                const APKMetaData& metadataA, const APKMetaData& metadataB) {
-        BuildIdenticalEntries(outIdenticalEntries, metadataA, metadataB);
-    }
+struct TestPatchGenerator : DeployPatchGenerator {
+    using DeployPatchGenerator::BuildIdenticalEntries;
+    using DeployPatchGenerator::DeployPatchGenerator;
 };
 
 TEST(DeployPatchGeneratorTest, IdenticalFileEntries) {
     std::string apkPath = GetTestFile("rotating_cube-release.apk");
-    APKMetaData metadataA = PatchUtils::GetAPKMetaData(apkPath.c_str());
-    TestPatchGenerator generator;
+    APKMetaData metadataA = PatchUtils::GetHostAPKMetaData(apkPath.c_str());
+    TestPatchGenerator generator(false);
     std::vector<DeployPatchGenerator::SimpleEntry> entries;
-    generator.GatherIdenticalEntries(entries, metadataA, metadataA);
+    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();
@@ -64,9 +61,28 @@
     // Create a patch that is 100% different.
     TemporaryFile output;
     DeployPatchGenerator generator(true);
-    generator.CreatePatch(apkPath.c_str(), "", output.fd);
+    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);
-}
\ No newline at end of file
+}
+
+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/patch_utils.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
index f11ddd1..2b00c80 100644
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
@@ -16,72 +16,94 @@
 
 #include "patch_utils.h"
 
-#include <androidfw/ZipFileRO.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::GetAPKMetaData(const char* apkPath) {
+APKMetaData PatchUtils::GetDeviceAPKMetaData(const APKDump& apk_dump) {
     APKMetaData apkMetaData;
-#undef open
-    std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
-#define open ___xxx_unix_open
-    if (zipFile == nullptr) {
-        printf("Could not open %s", apkPath);
-        exit(1);
-    }
-    void* cookie;
-    if (zipFile->startIteration(&cookie)) {
-        android::ZipEntryRO entry;
-        while ((entry = zipFile->nextEntry(cookie)) != NULL) {
-            char fileName[256];
-            // Make sure we have a file name.
-            // TODO: Handle filenames longer than 256.
-            if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
-                continue;
-            }
+    apkMetaData.set_absolute_path(apk_dump.absolute_path());
 
-            uint32_t uncompressedSize, compressedSize, crc32;
-            int64_t dataOffset;
-            zipFile->getEntryInfo(entry, nullptr, &uncompressedSize, &compressedSize, &dataOffset,
-                                  nullptr, &crc32);
-            APKEntry* apkEntry = apkMetaData.add_entries();
-            apkEntry->set_crc32(crc32);
-            apkEntry->set_filename(fileName);
-            apkEntry->set_compressedsize(compressedSize);
-            apkEntry->set_uncompressedsize(uncompressedSize);
-            apkEntry->set_dataoffset(dataOffset);
-        }
+    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 toLittleEndian = htole64(value);
-    WriteFdExactly(output, &toLittleEndian, sizeof(int64_t));
+    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 int BUFFER_SIZE = 128 * 1024;
+    constexpr static size_t BUFFER_SIZE = 128 * 1024;
     char buffer[BUFFER_SIZE];
     size_t transferAmount = 0;
     while (transferAmount != amount) {
-        long chunkAmount =
-                amount - transferAmount > BUFFER_SIZE ? BUFFER_SIZE : amount - transferAmount;
-        long readAmount = adb_read(input, buffer, chunkAmount);
+        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;
     }
-}
\ No newline at end of file
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.h b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
index 0ebfe8f..8dc9b9c 100644
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils.h
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
@@ -25,11 +25,18 @@
 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 GetAPKMetaData(const char* file);
+    static com::android::fastdeploy::APKMetaData GetHostAPKMetaData(const char* file);
     /**
      * Writes a fixed signature string to the header of the patch.
      */
@@ -39,8 +46,12 @@
      */
     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);
-};
\ No newline at end of file
+};
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
index a7eeebf..3ec5ab3 100644
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
@@ -23,10 +23,13 @@
 #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;
@@ -86,11 +89,56 @@
 
 TEST(PatchUtilsTest, GatherMetadata) {
     std::string apkFile = GetTestFile("rotating_cube-release.apk");
-    APKMetaData metadata = PatchUtils::GetAPKMetaData(apkFile.c_str());
+    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;
-    metadata.SerializeToString(&actualMetadata);
+    actual.SerializeToString(&actualMetadata);
+
+    expected.SerializeToString(&expectedMetadata);
+
     EXPECT_EQ(expectedMetadata, actualMetadata);
-}
\ No newline at end of file
+}
+
+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/proto/ApkEntry.proto b/adb/fastdeploy/proto/ApkEntry.proto
index 9460d15..d84c5a5 100644
--- a/adb/fastdeploy/proto/ApkEntry.proto
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -1,18 +1,26 @@
-syntax = "proto2";
+syntax = "proto3";
 
 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 {
-    required int64 crc32 = 1;
-    required string fileName = 2;
-    required int64 dataOffset = 3;
-    required int64 compressedSize = 4;
-    required int64 uncompressedSize = 5;
+    bytes md5 = 1;
+    int64 dataOffset = 2;
+    int64 dataSize = 3;
 }
 
 message APKMetaData {
-    repeated APKEntry entries = 1;
+    string absolute_path = 1;
+    repeated APKEntry entries = 2;
 }
diff --git a/adb/fastdeploy/testdata/helloworld5.apk b/adb/fastdeploy/testdata/helloworld5.apk
new file mode 100644
index 0000000..4a1539e
--- /dev/null
+++ b/adb/fastdeploy/testdata/helloworld5.apk
Binary files differ
diff --git a/adb/fastdeploy/testdata/helloworld7.apk b/adb/fastdeploy/testdata/helloworld7.apk
new file mode 100644
index 0000000..82c46df
--- /dev/null
+++ b/adb/fastdeploy/testdata/helloworld7.apk
Binary files differ
diff --git a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
index 0671bf3..52352ff 100644
--- a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
+++ b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
Binary files differ
diff --git a/adb/fastdeploy/testdata/sample.apk b/adb/fastdeploy/testdata/sample.apk
new file mode 100644
index 0000000..c316205
--- /dev/null
+++ b/adb/fastdeploy/testdata/sample.apk
Binary files differ
diff --git a/adb/fastdeploy/testdata/sample.cd b/adb/fastdeploy/testdata/sample.cd
new file mode 100644
index 0000000..5e5b4d4
--- /dev/null
+++ b/adb/fastdeploy/testdata/sample.cd
Binary files differ
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 75993b3..e78530c 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -757,6 +757,8 @@
 
 #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:")) {
         // serial number should follow "host:" and could be a host:port string.
         if (!internal::parse_host_service(&serial, &service, service)) {
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 987f994..466c2ce 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -601,6 +601,10 @@
     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) {
diff --git a/adb/test_device.py b/adb/test_device.py
index f95a5b3..57925e8 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -139,6 +139,25 @@
         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.')
@@ -884,6 +903,20 @@
             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/check_ms_os_desc.cpp b/adb/tools/check_ms_os_desc.cpp
index d4cfb61..8743ff7 100644
--- a/adb/tools/check_ms_os_desc.cpp
+++ b/adb/tools/check_ms_os_desc.cpp
@@ -158,6 +158,49 @@
     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) {
@@ -208,8 +251,10 @@
         check_ms_os_desc_v1(device_handle, *serial);
         fprintf(stderr, "found v1 OS descriptor for device %s\n", serial->c_str());
 
-        // TODO: Read BOS for MS OS Descriptor 2.0 descriptors:
+        // 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;
     }
diff --git a/adb/transport.h b/adb/transport.h
index 61a3d9a..c38cb1d 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -65,8 +65,10 @@
 extern const char* const kFeatureApex;
 // adbd has b/110953234 fixed.
 extern const char* const kFeatureFixedPushMkdir;
-// adbd supports android binder bridge (abb).
+// adbd supports android binder bridge (abb) in interactive mode using shell protocol.
 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;
diff --git a/base/Android.bp b/base/Android.bp
index 357ce01..f5000c1 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,8 +20,14 @@
         "-Wall",
         "-Werror",
         "-Wextra",
-        "-D_FILE_OFFSET_BITS=64",
     ],
+    target: {
+        android: {
+            cflags: [
+                "-D_FILE_OFFSET_BITS=64",
+            ],
+        },
+    },
 }
 
 cc_library_headers {
diff --git a/base/file.cpp b/base/file.cpp
index 3dfcfbb..6321fc6 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -49,29 +49,54 @@
 #include "android-base/unique_fd.h"
 #include "android-base/utf8.h"
 
+namespace {
+
 #ifdef _WIN32
-int mkstemp(char* template_name) {
-  if (_mktemp(template_name) == nullptr) {
+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) {
     return -1;
   }
+
   // Use open() to match the close() that TemporaryFile's destructor does.
   // Use O_BINARY to match base file APIs.
-  return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+  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;
 }
 
-char* mkdtemp(char* template_name) {
-  if (_mktemp(template_name) == nullptr) {
+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) {
     return nullptr;
   }
-  if (_mkdir(template_name) == -1) {
+
+  if (_wmkdir(path.c_str()) != 0) {
     return nullptr;
   }
-  return template_name;
+
+  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;
 }
 #endif
 
-namespace {
-
 std::string GetSystemTempDir() {
 #if defined(__ANDROID__)
   const auto* tmpdir = getenv("TMPDIR");
@@ -83,15 +108,20 @@
   // so try current directory if /data/local/tmp is not accessible.
   return ".";
 #elif defined(_WIN32)
-  char tmp_dir[MAX_PATH];
-  DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);  // checks TMP env
-  CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
-  CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+  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;
 
   // GetTempPath() returns a path with a trailing slash, but init()
   // does not expect that, so remove it.
-  CHECK_EQ(tmp_dir[result - 1], '\\');
-  tmp_dir[result - 1] = '\0';
+  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";
+
   return tmp_dir;
 #else
   const auto* tmpdir = getenv("TMPDIR");
@@ -127,7 +157,11 @@
 
 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() {
@@ -167,7 +201,11 @@
 
 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 {
diff --git a/base/file_test.cpp b/base/file_test.cpp
index f64e81c..120228d 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -16,18 +16,25 @@
 
 #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;
@@ -38,7 +45,7 @@
 
 TEST(file, ReadFileToString_WriteStringToFile) {
   TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_NE(tf.fd, -1) << tf.path;
   ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
     << strerror(errno);
   std::string s;
@@ -70,7 +77,7 @@
 #if !defined(_WIN32)
 TEST(file, WriteStringToFile2) {
   TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_NE(tf.fd, -1) << tf.path;
   ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
                                                getuid(), getgid()))
       << strerror(errno);
@@ -86,9 +93,92 @@
 }
 #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_TRUE(tf.fd != -1);
+  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);
@@ -100,7 +190,7 @@
 
 TEST(file, WriteFully) {
   TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_NE(tf.fd, -1) << tf.path;
   ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
 
   ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
@@ -119,7 +209,7 @@
 
 TEST(file, RemoveFileIfExists) {
   TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_NE(tf.fd, -1) << tf.path;
   close(tf.fd);
   tf.fd = -1;
   std::string err;
@@ -253,7 +343,7 @@
 
 TEST(file, ReadFileToString_capacity) {
   TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_NE(tf.fd, -1) << tf.path;
 
   // For a huge file, the overhead should still be small.
   std::string s;
@@ -280,7 +370,7 @@
 
 TEST(file, ReadFileToString_capacity_0) {
   TemporaryFile tf;
-  ASSERT_TRUE(tf.fd != -1);
+  ASSERT_NE(tf.fd, -1) << tf.path;
 
   // 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/include/android-base/endian.h b/base/include/android-base/endian.h
index 2d0f614..8fa6365 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -51,8 +51,10 @@
 /* macOS has some of the basics. */
 #include <sys/_endian.h>
 #else
-/* Windows has even less. */
+/* 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)
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index dd24aac..d08a59f 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -311,6 +311,7 @@
     {"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
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 1a5b435..7e35a2f 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -195,7 +195,10 @@
     return false;
   }
 
-  InterceptRequest req = {.pid = pid, .dump_type = dump_type};
+  InterceptRequest req = {
+      .dump_type = dump_type,
+      .pid = pid,
+  };
   if (!set_timeout(sockfd)) {
     PLOG(ERROR) << "libdebugger_client: failed to set timeout";
     return false;
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index 9c2f0d6..2545cd6 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 kB should be enough for everyone.
+  // 64 MiB 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(250ms);
+  std::this_thread::sleep_for(1s);
 
   ASSERT_TRUE(
-      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write)));
+      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 60000, 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/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index fbc8b97..99729dc 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -101,7 +101,10 @@
     FAIL() << "failed to contact tombstoned: " << strerror(errno);
   }
 
-  InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type};
+  InterceptRequest req = {
+      .dump_type = intercept_type,
+      .pid = target_pid,
+  };
 
   unique_fd output_pipe_write;
   if (!Pipe(output_fd, &output_pipe_write)) {
@@ -176,7 +179,7 @@
   if (crasher_pid != -1) {
     kill(crasher_pid, SIGKILL);
     int status;
-    waitpid(crasher_pid, &status, WUNTRACED);
+    TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, WUNTRACED));
   }
 
   android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
@@ -195,8 +198,7 @@
 void CrasherTest::FinishIntercept(int* result) {
   InterceptResponse response;
 
-  // Timeout for tombstoned intercept is 10 seconds.
-  ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
+  ssize_t rc = TIMEOUT(30, read(intercept_fd.get(), &response, sizeof(response)));
   if (rc == -1) {
     FAIL() << "failed to read response from tombstoned: " << strerror(errno);
   } else if (rc == 0) {
@@ -233,7 +235,7 @@
     FAIL() << "crasher pipe uninitialized";
   }
 
-  ssize_t rc = write(crasher_pipe.get(), "\n", 1);
+  ssize_t rc = TEMP_FAILURE_RETRY(write(crasher_pipe.get(), "\n", 1));
   if (rc == -1) {
     FAIL() << "failed to write to crasher pipe: " << strerror(errno);
   } else if (rc == 0) {
@@ -243,7 +245,7 @@
 
 void CrasherTest::AssertDeath(int signo) {
   int status;
-  pid_t pid = TIMEOUT(10, waitpid(crasher_pid, &status, 0));
+  pid_t pid = TIMEOUT(30, waitpid(crasher_pid, &status, 0));
   if (pid != crasher_pid) {
     printf("failed to wait for crasher (expected pid %d, return value %d): %s\n", crasher_pid, pid,
            strerror(errno));
@@ -441,7 +443,7 @@
   FinishCrasher();
 
   int status;
-  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
+  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, WUNTRACED)));
   ASSERT_TRUE(WIFSTOPPED(status));
   ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
 
@@ -603,13 +605,15 @@
   policy += "\nclone: 1";
   policy += "\nsigaltstack: 1";
   policy += "\nnanosleep: 1";
+  policy += "\ngetrlimit: 1";
+  policy += "\nugetrlimit: 1";
 
   FILE* tmp_file = tmpfile();
   if (!tmp_file) {
     PLOG(FATAL) << "tmpfile failed";
   }
 
-  unique_fd tmp_fd(dup(fileno(tmp_file)));
+  unique_fd tmp_fd(TEMP_FAILURE_RETRY(dup(fileno(tmp_file))));
   if (!android::base::WriteStringToFd(policy, tmp_fd.get())) {
     PLOG(FATAL) << "failed to write policy to tmpfile";
   }
@@ -822,7 +826,7 @@
   FinishCrasher();
 
   int status;
-  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, 0)));
   ASSERT_TRUE(WIFSTOPPED(status));
   ASSERT_EQ(SIGABRT, WSTOPSIG(status));
 
@@ -837,7 +841,7 @@
   regex += R"( \(.+debuggerd_test)";
   ASSERT_MATCH(result, regex.c_str());
 
-  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, 0)));
   ASSERT_TRUE(WIFSTOPPED(status));
   ASSERT_EQ(SIGABRT, WSTOPSIG(status));
 
@@ -851,7 +855,7 @@
 
   StartProcess([]() {
     android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
-    unique_fd fd(open("/dev/null", O_RDONLY | O_CLOEXEC));
+    unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY | O_CLOEXEC)));
     if (fd == -1) {
       abort();
     }
@@ -886,13 +890,13 @@
     raise(DEBUGGER_SIGNAL);
 
     errno = 0;
-    rc = waitpid(-1, &status, __WALL | __WNOTHREAD);
+    rc = TEMP_FAILURE_RETRY(waitpid(-1, &status, __WALL | __WNOTHREAD));
     if (rc != -1 || errno != ECHILD) {
       errx(2, "second waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
     }
     _exit(0);
   } else {
-    rc = waitpid(forkpid, &status, 0);
+    rc = TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0));
     ASSERT_EQ(forkpid, rc);
     ASSERT_TRUE(WIFEXITED(status));
     ASSERT_EQ(0, WEXITSTATUS(status));
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 598ea85..b90ca80 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 = {
-      .pseudothread_tid = -1,
       .crashing_tid = __gettid(),
+      .pseudothread_tid = -1,
       .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 88c206f..9dea7ac 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/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index bbeb181..d09b8e8 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", 10),
+                            GetIntProperty("tombstoned.max_tombstone_count", 32),
                             1 /* max_concurrent_dumps */);
     return &queue;
   }
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 978eed0..f452a64 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -143,6 +143,10 @@
     static_libs: [
         "libhealthhalutils",
     ],
+
+    header_libs: [
+        "libsnapshot_headers",
+    ]
 }
 
 cc_defaults {
@@ -185,7 +189,11 @@
     // will violate ODR.
     shared_libs: [],
 
-    header_libs: ["bootimg_headers"],
+    header_libs: [
+        "avb_headers",
+        "bootimg_headers",
+    ],
+
     static_libs: [
         "libziparchive",
         "libsparse",
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 99854c9..102ebdb 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -32,6 +32,7 @@
 #include <fstab/fstab.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
 #include <sparse/sparse.h>
 
 #include "fastboot_device.h"
@@ -171,6 +172,11 @@
         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/fastboot.cpp b/fastboot/fastboot.cpp
index 4737ae4..2fe3b1a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -51,6 +51,7 @@
 #include <utility>
 #include <vector>
 
+#include <android-base/endian.h>
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
@@ -59,6 +60,7 @@
 #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>
@@ -874,7 +876,7 @@
         return false;
     }
 
-    if (sparse_file* s = sparse_file_import_auto(fd, false, false)) {
+    if (sparse_file* s = sparse_file_import(fd, false, false)) {
         buf->image_size = sparse_file_len(s, false, false);
         sparse_file_destroy(s);
     } else {
@@ -919,33 +921,50 @@
     return load_buf_fd(fd.release(), buf);
 }
 
-static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
+static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_boot) {
     // 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[123] |= 0x01;
+        data[flags_offset] |= 0x01;
     }
     if (g_disable_verification) {
-        data[123] |= 0x02;
+        data[flags_offset] |= 0x02;
     }
 
+    int fd = make_temporary_fd("vbmeta rewriting");
     if (!android::base::WriteStringToFd(data, fd)) {
         die("Failed writing to modified vbmeta");
     }
@@ -954,14 +973,25 @@
     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) &&
-        (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b")) {
-        rewrite_vbmeta_buffer(buf);
+    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 */ );
+        }
     }
 
     switch (buf->type) {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 7a0d019..4ba1c49 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -222,13 +222,11 @@
         } else {
             LINFO << "Running " << E2FSCK_BIN << " on " << realpath(blk_device);
             if (should_force_check(*fs_stat)) {
-                ret = android_fork_execvp_ext(
-                    ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
-                    true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+                ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_forced_argv), e2fsck_forced_argv,
+                                          &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
             } else {
-                ret = android_fork_execvp_ext(
-                    ARRAY_SIZE(e2fsck_argv), const_cast<char**>(e2fsck_argv), &status, true,
-                    LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+                ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, &status, false,
+                                          LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
             }
 
             if (ret < 0) {
@@ -246,14 +244,12 @@
 
         if (should_force_check(*fs_stat)) {
             LINFO << "Running " << F2FS_FSCK_BIN << " -f " << realpath(blk_device);
-            ret = android_fork_execvp_ext(
-                ARRAY_SIZE(f2fs_fsck_forced_argv), const_cast<char**>(f2fs_fsck_forced_argv), &status,
-                true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
+                                      &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
         } else {
             LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
-            ret = android_fork_execvp_ext(
-                ARRAY_SIZE(f2fs_fsck_argv), const_cast<char**>(f2fs_fsck_argv), &status, true,
-                LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
+                                      LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
         }
         if (ret < 0) {
             /* No need to check for error in fork, we can't really handle it now */
@@ -331,8 +327,7 @@
 static bool run_tune2fs(const char* argv[], int argc) {
     int ret;
 
-    ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true,
-                                  LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0);
+    ret = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_KLOG, true, nullptr);
     return ret == 0;
 }
 
@@ -852,37 +847,22 @@
     }
 }
 
-static bool call_vdc(const std::vector<std::string>& args) {
+static bool call_vdc(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 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);
+    int err = logwrap_fork_execvp(argv.size(), argv.data(), ret, false, LOG_ALOG, false, nullptr);
     if (err != 0) {
         LOG(ERROR) << "vdc call failed with error code: " << err;
         return false;
     }
     LOG(DEBUG) << "vdc finished successfully";
-    *ret = WEXITSTATUS(*ret);
+    if (ret != nullptr) {
+        *ret = WEXITSTATUS(*ret);
+    }
     return true;
 }
 
@@ -914,11 +894,11 @@
         }
 
         if (entry->fs_mgr_flags.checkpoint_blk) {
-            call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device});
+            call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device}, nullptr);
         }
 
         if (needs_checkpoint_ == UNKNOWN &&
-            !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
+            !call_vdc({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
             LERROR << "Failed to find if checkpointing is needed. Assuming no.";
             needs_checkpoint_ = NO;
         }
@@ -1193,7 +1173,8 @@
                 encryptable = status;
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
                     if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
-                                   attempted_entry.mount_point})) {
+                                   attempted_entry.mount_point},
+                                  nullptr)) {
                         LERROR << "Encryption failed";
                         return FS_MGR_MNTALL_FAIL;
                     }
@@ -1265,7 +1246,8 @@
         } 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})) {
+                           attempted_entry.mount_point},
+                          nullptr)) {
                 ++error_count;
             }
             encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@@ -1615,10 +1597,8 @@
                 MKSWAP_BIN,
                 entry.blk_device.c_str(),
         };
-        int err = 0;
-        int status;
-        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), const_cast<char**>(mkswap_argv),
-                                      &status, true, LOG_KLOG, false, nullptr, nullptr, 0);
+        int err = logwrap_fork_execvp(ARRAY_SIZE(mkswap_argv), mkswap_argv, nullptr, false,
+                                      LOG_KLOG, false, nullptr);
         if (err) {
             LERROR << "mkswap failed for " << entry.blk_device;
             ret = false;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index ea799ce..0dcb9fe 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -79,22 +79,22 @@
     return true;
 }
 
-bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
-                   const LpMetadataPartition& partition, const std::string& super_device,
-                   DmTable* table) {
+bool CreateDmTableInternal(const CreateLogicalPartitionParams& params, DmTable* table) {
+    const auto& super_device = params.block_device;
+
     uint64_t sector = 0;
-    for (size_t i = 0; i < partition.num_extents; i++) {
-        const auto& extent = metadata.extents[partition.first_extent_index + i];
+    for (size_t i = 0; i < params.partition->num_extents; i++) {
+        const auto& extent = params.metadata->extents[params.partition->first_extent_index + i];
         std::unique_ptr<DmTarget> target;
         switch (extent.target_type) {
             case LP_TARGET_TYPE_ZERO:
                 target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
                 break;
             case LP_TARGET_TYPE_LINEAR: {
-                const auto& block_device = metadata.block_devices[extent.target_source];
+                const auto& block_device = params.metadata->block_devices[extent.target_source];
                 std::string dev_string;
-                if (!GetPhysicalPartitionDevicePath(opener, metadata, block_device, super_device,
-                                                    &dev_string)) {
+                if (!GetPhysicalPartitionDevicePath(*params.partition_opener, *params.metadata,
+                                                    block_device, super_device, &dev_string)) {
                     LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
                     return false;
                 }
@@ -111,12 +111,21 @@
         }
         sector += extent.num_sectors;
     }
-    if (partition.attributes & LP_PARTITION_ATTR_READONLY) {
+    if (params.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);
+}
+
 bool CreateLogicalPartitions(const std::string& block_device) {
     uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
     auto metadata = ReadMetadata(block_device.c_str(), slot);
@@ -160,6 +169,11 @@
         return false;
     }
 
+    if (!partition_opener) {
+        owned->partition_opener = std::make_unique<PartitionOpener>();
+        partition_opener = owned->partition_opener.get();
+    }
+
     // Read metadata if needed.
     if (!metadata) {
         if (!metadata_slot) {
@@ -167,7 +181,8 @@
             return false;
         }
         auto slot = *metadata_slot;
-        if (owned->metadata = ReadMetadata(block_device, slot); !owned->metadata) {
+        if (owned->metadata = ReadMetadata(*partition_opener, block_device, slot);
+            !owned->metadata) {
             LOG(ERROR) << "Could not read partition table for: " << block_device;
             return false;
         }
@@ -195,11 +210,6 @@
         return false;
     }
 
-    if (!partition_opener) {
-        owned->partition_opener = std::make_unique<PartitionOpener>();
-        partition_opener = owned->partition_opener.get();
-    }
-
     if (device_name.empty()) {
         device_name = partition_name;
     }
@@ -212,13 +222,9 @@
     if (!params.InitDefaults(&owned_data)) return false;
 
     DmTable table;
-    if (!CreateDmTable(*params.partition_opener, *params.metadata, *params.partition,
-                       params.block_device, &table)) {
+    if (!CreateDmTableInternal(params, &table)) {
         return false;
     }
-    if (params.force_writable) {
-        table.set_readonly(false);
-    }
 
     DeviceMapper& dm = DeviceMapper::Instance();
     if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 1c6652a..acf4d7b 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 = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), NULL,
-                                 true, LOG_KLOG, true, nullptr, nullptr, 0);
+    rc = logwrap_fork_execvp(arraysize(mke2fs_args), mke2fs_args, nullptr, false, LOG_KLOG, true,
+                             nullptr);
     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 = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
-                                 NULL, true, LOG_KLOG, true, nullptr, nullptr, 0);
+    rc = logwrap_fork_execvp(arraysize(e2fsdroid_args), e2fsdroid_args, nullptr, false, LOG_KLOG,
+                             true, nullptr);
     if (rc) {
         LERROR << "e2fsdroid returned " << rc;
     }
@@ -119,8 +119,7 @@
     };
     // clang-format on
 
-    return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
-                                   LOG_KLOG, true, nullptr, nullptr, 0);
+    return logwrap_fork_execvp(arraysize(args), args, nullptr, false, LOG_KLOG, true, nullptr);
 }
 
 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 4dbacd7..2ff5243 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -36,6 +36,7 @@
 
 #include "fs_mgr_priv.h"
 
+using android::base::EndsWith;
 using android::base::ParseByteCount;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
@@ -111,13 +112,16 @@
 };
 
 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.
+    // The fileencryption flag is followed by an = and 1 to 3 colon-separated fields:
+    //
+    // 1. Contents encryption mode
+    // 2. Filenames encryption mode (defaults to "aes-256-cts" or "adiantum"
+    //    depending on the contents encryption mode)
+    // 3. Encryption policy version (defaults to "v1". Use "v2" on new devices.)
     entry->fs_mgr_flags.file_encryption = true;
 
     auto parts = Split(arg, ":");
-    if (parts.empty() || parts.size() > 2) {
+    if (parts.empty() || parts.size() > 3) {
         LWARNING << "Warning: fileencryption= flag malformed: " << arg;
         return;
     }
@@ -136,7 +140,7 @@
 
     entry->file_contents_mode = parts[0];
 
-    if (parts.size() == 2) {
+    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: "
@@ -150,6 +154,16 @@
     } else {
         entry->file_names_mode = "aes-256-cts";
     }
+
+    if (parts.size() >= 3) {
+        if (!android::base::StartsWith(parts[2], 'v') ||
+            !android::base::ParseInt(&parts[2][1], &entry->file_policy_version)) {
+            LWARNING << "fileencryption= flag malformed, unknown options: " << arg;
+            return;
+        }
+    } else {
+        entry->file_policy_version = 1;
+    }
 }
 
 bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
@@ -287,6 +301,7 @@
             entry->key_loc = arg;
             entry->file_contents_mode = "aes-256-xts";
             entry->file_names_mode = "aes-256-cts";
+            entry->file_policy_version = 1;
         } else if (StartsWith(flag, "max_comp_streams=")) {
             if (!ParseInt(arg, &entry->max_comp_streams)) {
                 LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
@@ -598,7 +613,7 @@
     return boot_devices;
 }
 
-FstabEntry BuildGsiUserdataFstabEntry() {
+FstabEntry BuildDsuUserdataFstabEntry() {
     constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
 
     FstabEntry userdata = {
@@ -627,7 +642,12 @@
     return false;
 }
 
-void TransformFstabForGsi(Fstab* fstab) {
+}  // 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
     // Inherit fstab properties for userdata.
     FstabEntry userdata;
     if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
@@ -639,19 +659,76 @@
             userdata.key_dir += "/gsi";
         }
     } else {
-        userdata = BuildGsiUserdataFstabEntry();
-    }
-
-    if (EraseFstabEntry(fstab, "/system")) {
-        fstab->emplace_back(BuildGsiSystemFstabEntry());
+        userdata = BuildDsuUserdataFstabEntry();
     }
 
     if (EraseFstabEntry(fstab, "/data")) {
         fstab->emplace_back(userdata);
     }
-}
 
-}  // namespace
+    // 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);
+            }
+        }
+    }
+}
 
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
     auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
@@ -667,7 +744,9 @@
         return false;
     }
     if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
-        TransformFstabForGsi(fstab);
+        std::string lp_names;
+        ReadFileToString(gsi::kGsiLpNamesFile, &lp_names);
+        TransformFstabForDsu(fstab, Split(lp_names, ","));
     }
 
     SkipMountingPartitions(fstab);
@@ -779,6 +858,21 @@
     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";
@@ -798,23 +892,6 @@
     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/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey",
-            .logical_partition_name = "system"};
-    system.fs_mgr_flags.wait = true;
-    system.fs_mgr_flags.logical = true;
-    system.fs_mgr_flags.first_stage_mount = true;
-    return system;
-}
-
 std::string GetVerityDeviceName(const FstabEntry& entry) {
     std::string base_device;
     if (entry.mount_point == "/") {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 358c980..0579a3d 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -782,6 +782,7 @@
     } 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) {
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index a912208..f3d09fb 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -105,9 +105,7 @@
 bool DestroyLogicalPartition(const std::string& name);
 
 // Helper for populating a DmTable for a logical partition.
-bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
-                   const LpMetadataPartition& partition, const std::string& super_device,
-                   android::dm::DmTable* table);
+bool CreateDmTable(CreateLogicalPartitionParams params, android::dm::DmTable* table);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index c7193ab..3c517dc 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -47,6 +47,7 @@
     off64_t reserved_size = 0;
     std::string file_contents_mode;
     std::string file_names_mode;
+    int file_policy_version = 0;
     off64_t erase_blk_size = 0;
     off64_t logical_blk_size = 0;
     std::string sysfs_path;
@@ -101,9 +102,18 @@
 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);
 
-// Helper method to build a GSI fstab entry for mounting /system.
-FstabEntry BuildGsiSystemFstabEntry();
+// 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);
 
 std::set<std::string> GetBootDevices();
 
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 4cdea71..8a924d5 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -44,8 +44,17 @@
     },
 }
 
-cc_test {
-    name: "libdm_test",
+filegroup {
+    name: "libdm_test_srcs",
+    srcs: [
+        "dm_test.cpp",
+        "loop_control_test.cpp",
+        "test_util.cpp",
+    ],
+}
+
+cc_defaults {
+    name: "libdm_defaults",
     defaults: ["fs_mgr_defaults"],
     static_libs: [
         "libdm",
@@ -54,9 +63,35 @@
         "libfs_mgr",
         "liblog",
     ],
-    srcs: [
-        "dm_test.cpp",
-        "loop_control_test.cpp",
-        "test_util.cpp",
-    ]
+    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",
+  ],
 }
diff --git a/fs_mgr/libdm/dm_linear_fuzzer.cpp b/fs_mgr/libdm/dm_linear_fuzzer.cpp
new file mode 100644
index 0000000..8462901
--- /dev/null
+++ b/fs_mgr/libdm/dm_linear_fuzzer.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 15c7ce1..efe03ab 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -26,6 +26,7 @@
     if (!target->Valid()) {
         return false;
     }
+    num_sectors_ += target->size();
     targets_.push_back(std::move(target));
     return true;
 }
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index eed21dc..ed2fa83 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -47,50 +47,6 @@
     ASSERT_TRUE(dm.GetTargetByName("linear", &info));
 }
 
-// Helper to ensure that device mapper devices are released.
-class TempDevice {
-  public:
-    TempDevice(const std::string& name, const DmTable& table)
-        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
-        valid_ = dm_.CreateDevice(name, table, &path_, 5s);
-    }
-    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_;
-};
-
 TEST(libdm, DmLinear) {
     unique_fd tmp1(CreateTempFile("file_1", 4096));
     ASSERT_GE(tmp1, 0);
@@ -114,6 +70,7 @@
     ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));
     ASSERT_TRUE(table.Emplace<DmTargetLinear>(1, 1, loop_b.device(), 0));
     ASSERT_TRUE(table.valid());
+    ASSERT_EQ(2u, table.num_sectors());
 
     TempDevice dev("libdm-test-dm-linear", table);
     ASSERT_TRUE(dev.valid());
@@ -176,6 +133,7 @@
     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());
@@ -292,6 +250,7 @@
     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());
@@ -303,6 +262,7 @@
             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());
@@ -322,6 +282,7 @@
             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));
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
index eeed6b5..ad53c11 100644
--- a/fs_mgr/libdm/include/libdm/loop_control.h
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -64,7 +64,8 @@
   public:
     // Create a loop device for the given file descriptor. It is closed when
     // LoopDevice is destroyed only if auto_close is true.
-    LoopDevice(int fd, const std::chrono::milliseconds& timeout_ms, bool auto_close = false);
+    LoopDevice(android::base::borrowed_fd fd, const std::chrono::milliseconds& timeout_ms,
+               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);
@@ -81,8 +82,8 @@
   private:
     void Init(const std::chrono::milliseconds& timeout_ms);
 
-    android::base::unique_fd fd_;
-    bool owns_fd_;
+    android::base::borrowed_fd fd_;
+    android::base::unique_fd owned_fd_;
     std::string device_;
     LoopControl control_;
     bool valid_ = false;
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
index edc9a45..2e40a18 100644
--- a/fs_mgr/libdm/loop_control.cpp
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -133,18 +133,23 @@
     return true;
 }
 
-LoopDevice::LoopDevice(int fd, const std::chrono::milliseconds& timeout_ms, bool auto_close)
-    : fd_(fd), owns_fd_(auto_close) {
+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), owns_fd_(true) {
-    fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
-    if (fd_ < -1) {
+    : fd_(-1), owned_fd_(-1) {
+    owned_fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));
+    if (owned_fd_ == -1) {
         PLOG(ERROR) << "open failed for " << path;
         return;
     }
+    fd_ = owned_fd_;
     Init(timeout_ms);
 }
 
@@ -152,13 +157,10 @@
     if (valid()) {
         control_.Detach(device_);
     }
-    if (!owns_fd_) {
-        (void)fd_.release();
-    }
 }
 
 void LoopDevice::Init(const std::chrono::milliseconds& timeout_ms) {
-    valid_ = control_.Attach(fd_, timeout_ms, &device_);
+    valid_ = control_.Attach(fd_.get(), timeout_ms, &device_);
 }
 
 }  // namespace dm
diff --git a/fs_mgr/libdm/test_util.h b/fs_mgr/libdm/test_util.h
index 96b051c..6671364 100644
--- a/fs_mgr/libdm/test_util.h
+++ b/fs_mgr/libdm/test_util.h
@@ -20,8 +20,12 @@
 #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 {
 
@@ -29,6 +33,50 @@
 // 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/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 0d342d3..784eb9c 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)
-        .mount_point = "/system",
-        .fs_type = "ext4",
-        .logical_partition_name = "system",
+            .blk_device = "/dev/block/dm-1",  // a dm-linear device (logical)
+            .logical_partition_name = "system",
+            .mount_point = "/system",
+            .fs_type = "ext4",
     };
 
     // Logical partitions.
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index f0142bb..ea0fca8 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -17,7 +17,6 @@
 liblp_lib_deps = [
     "libbase",
     "liblog",
-    "libcrypto",
     "libcrypto_utils",
     "libsparse",
     "libext4_utils",
@@ -41,7 +40,9 @@
         "utility.cpp",
         "writer.cpp",
     ],
-    shared_libs: liblp_lib_deps,
+    shared_libs: [
+        "libcrypto",
+    ] + liblp_lib_deps,
     target: {
         windows: {
             enabled: true,
@@ -55,6 +56,17 @@
     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",
     defaults: ["fs_mgr_defaults"],
@@ -66,25 +78,29 @@
         "libgmock",
         "libfs_mgr",
         "liblp",
+        "libcrypto_static",
     ] + liblp_lib_deps,
-    stl: "libc++_static",
-    srcs: [
-        "builder_test.cpp",
-        "device_test.cpp",
-        "io_test.cpp",
-        "test_partition_opener.cpp",
-        "utility_test.cpp",
+    header_libs: [
+        "libstorage_literals_headers",
     ],
+    stl: "libc++_static",
+    srcs: [":liblp_test_srcs"],
 }
 
 cc_test {
     name: "liblp_test",
     defaults: ["liblp_test_defaults"],
     test_config: "liblp_test.xml",
-    test_suites: [
-        "device-tests",
-        "vts-core",
-    ],
+    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 {
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index c5d6a3b..54350a5 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -40,6 +40,10 @@
     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;
@@ -96,6 +100,20 @@
     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_) {
@@ -153,7 +171,8 @@
 std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,
                                                                const std::string& source_partition,
                                                                uint32_t source_slot_number,
-                                                               uint32_t target_slot_number) {
+                                                               uint32_t target_slot_number,
+                                                               bool always_keep_source_slot) {
     auto metadata = ReadMetadata(opener, source_partition, source_slot_number);
     if (!metadata) {
         return nullptr;
@@ -171,7 +190,8 @@
         }
     }
 
-    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+    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;
@@ -486,7 +506,7 @@
     return total;
 }
 
-void MetadataBuilder::RemovePartition(const std::string& name) {
+void MetadataBuilder::RemovePartition(std::string_view name) {
     for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
         if ((*iter)->name() == name) {
             partitions_.erase(iter);
@@ -602,6 +622,10 @@
     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) {
     uint64_t space_needed = aligned_size - partition->size();
@@ -1114,7 +1138,7 @@
     return true;
 }
 
-std::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(const std::string& group_name) {
+std::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(std::string_view group_name) {
     std::vector<Partition*> partitions;
     for (const auto& partition : partitions_) {
         if (partition->group_name() == group_name) {
@@ -1168,5 +1192,9 @@
                    : "";
 }
 
+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 bd41f59..a67ffa7 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -17,11 +17,13 @@
 #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::_;
@@ -591,13 +593,6 @@
     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);
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 58a88b5..6b842b3 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -17,6 +17,7 @@
 #include "images.h"
 
 #include <limits.h>
+#include <sys/stat.h>
 
 #include <android-base/file.h>
 
@@ -27,12 +28,45 @@
 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) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 6f2ab75..1e9d636 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -33,10 +33,11 @@
 namespace fs_mgr {
 
 class LinearExtent;
+struct Interval;
 
 // By default, partitions are aligned on a 1MiB boundary.
-static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
-static const uint32_t kDefaultBlockSize = 4096;
+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";
@@ -74,6 +75,8 @@
         return sector >= physical_sector_ && sector < end_sector();
     }
 
+    Interval AsInterval() const;
+
   private:
     uint32_t device_index_;
     uint64_t physical_sector_;
@@ -127,6 +130,12 @@
     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; }
@@ -156,6 +165,8 @@
         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);
@@ -198,10 +209,13 @@
     // 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);
+                                                         uint32_t target_slot_number,
+                                                         bool always_keep_source_slot = false);
 
     // Import an existing table for modification. If the table is not valid, for
     // example it contains duplicate partition names, then nullptr is returned.
@@ -249,7 +263,7 @@
     Partition* AddPartition(const std::string& name, uint32_t attributes);
 
     // Delete a partition by name if it exists.
-    void RemovePartition(const std::string& name);
+    void RemovePartition(std::string_view name);
 
     // Find a partition by name. If no partition is found, nullptr is returned.
     Partition* FindPartition(std::string_view name);
@@ -278,7 +292,7 @@
                          const std::vector<Interval>& free_region_hint = {});
 
     // Return the list of partitions belonging to a group.
-    std::vector<Partition*> ListPartitionsInGroup(const std::string& group_name);
+    std::vector<Partition*> ListPartitionsInGroup(std::string_view 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
@@ -325,6 +339,8 @@
     // 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;
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 135a1b3..cd860cd 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -70,8 +70,15 @@
                           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.
+// 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,
                       const std::map<std::string, std::string>& images, bool sparsify);
 bool WriteToImageFile(const std::string& file, const LpMetadata& metadata);
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index afcce8f..48c5c83 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -205,9 +205,9 @@
 #endif
 }
 
-base::unique_fd GetControlFileOrOpen(const char* path, int flags) {
+base::unique_fd GetControlFileOrOpen(std::string_view path, int flags) {
 #if defined(__ANDROID__)
-    int fd = android_get_control_file(path);
+    int fd = android_get_control_file(path.data());
     if (fd >= 0) {
         int newfd = TEMP_FAILURE_RETRY(dup(fd));
         if (newfd >= 0) {
@@ -216,7 +216,7 @@
         PERROR << "Cannot dup fd for already controlled file: " << path << ", reopening...";
     }
 #endif
-    return base::unique_fd(open(path, flags));
+    return base::unique_fd(open(path.data(), flags));
 }
 
 bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 25ab66b..0661769 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -21,6 +21,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <string>
+#include <string_view>
+
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
@@ -94,7 +97,7 @@
 // Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
 bool SetBlockReadonly(int fd, bool readonly);
 
-::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
+::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,
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 8df9c52..9256a16 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -25,11 +25,15 @@
     shared_libs: [
         "libbase",
         "liblog",
+        "liblp",
     ],
     static_libs: [
+        "libcutils",
         "libdm",
         "libfs_mgr",
+        "libfstab",
         "liblp",
+        "update_metadata-protos",
     ],
     whole_static_libs: [
         "libext2_uuid",
@@ -39,23 +43,54 @@
     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"],
+    defaults: [
+        "libsnapshot_defaults",
+        "libsnapshot_hal_deps",
+    ],
     srcs: [":libsnapshot_sources"],
     whole_static_libs: [
         "libfiemap_binder",
@@ -63,7 +98,7 @@
 }
 
 cc_library_static {
-    name: "libsnapshot_nobinder",
+    name: "libsnapshot_init",
     defaults: ["libsnapshot_defaults"],
     srcs: [":libsnapshot_sources"],
     recovery_available: true,
@@ -72,23 +107,77 @@
     ],
 }
 
+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: [
-        "libcutils",
-        "libcrypto",
+        "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/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
new file mode 100644
index 0000000..629c3a4
--- /dev/null
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -0,0 +1,87 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+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
new file mode 100644
index 0000000..4903de1
--- /dev/null
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -0,0 +1,133 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+
+#include <vector>
+
+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/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index c41a951..fcaa73a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -17,14 +17,20 @@
 #include <stdint.h>
 
 #include <chrono>
+#include <map>
 #include <memory>
+#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>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -43,8 +49,26 @@
 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,
@@ -72,11 +96,15 @@
     // 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 LpMetadata = android::fs_mgr::LpMetadata;
     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.
@@ -86,8 +114,11 @@
         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;
     };
 
     ~SnapshotManager();
@@ -109,8 +140,9 @@
     // will fail if GetUpdateState() != None.
     bool BeginUpdate();
 
-    // Cancel an update; any snapshots will be deleted. This will fail if the
-    // state != Initiated or None.
+    // 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
@@ -153,6 +185,18 @@
     //   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();
@@ -161,6 +205,9 @@
     // call to CreateLogicalPartitions when snapshots are present.
     bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
 
+    // Dump debug information.
+    bool Dump(std::ostream& os);
+
   private:
     FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -173,7 +220,13 @@
     FRIEND_TEST(SnapshotTest, Merge);
     FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
+    FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+    FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     friend class SnapshotTest;
+    friend class SnapshotUpdateTest;
+    friend struct AutoDeleteCowImage;
+    friend struct AutoDeleteSnapshot;
+    friend struct PartitionCowCreator;
 
     using DmTargetSnapshot = android::dm::DmTargetSnapshot;
     using IImageManager = android::fiemap::IImageManager;
@@ -211,22 +264,6 @@
     std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
     bool Truncate(LockedFile* file);
 
-    enum class SnapshotState : int { None, Created, Merging, MergeCompleted };
-    static std::string to_string(SnapshotState state);
-
-    // This state is persisted per-snapshot in /metadata/ota/snapshots/.
-    struct SnapshotStatus {
-        SnapshotState state = SnapshotState::None;
-        uint64_t device_size = 0;
-        uint64_t snapshot_size = 0;
-        uint64_t cow_partition_size = 0;
-        uint64_t cow_file_size = 0;
-
-        // These are non-zero when merging.
-        uint64_t sectors_allocated = 0;
-        uint64_t metadata_sectors = 0;
-    };
-
     // 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().
@@ -241,10 +278,9 @@
     // 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 and COW partition sizes
-    // must be a multiple of the sector size (512 bytes). COW file size will be rounded up
-    // to the nearest sector.
-    bool CreateSnapshot(LockedFile* lock, const std::string& name, SnapshotStatus status);
+    // 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().
@@ -262,8 +298,7 @@
                      std::string* dev_path);
 
     // Map a COW image that was previous created with CreateCowImage.
-    bool MapCowImage(const std::string& name, const std::chrono::milliseconds& timeout_ms,
-                     std::string* cow_image_device);
+    bool 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.
@@ -326,8 +361,7 @@
     UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
 
     // Interact with status files under /metadata/ota/snapshots.
-    bool WriteSnapshotStatus(LockedFile* lock, const std::string& name,
-                             const SnapshotStatus& status);
+    bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
     bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
     std::string GetSnapshotStatusFilePath(const std::string& name);
 
@@ -344,6 +378,45 @@
     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_;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
new file mode 100644
index 0000000..eedc1cd
--- /dev/null
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -0,0 +1,130 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES 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 "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 {
+
+// Round |d| up to a multiple of |block_size|.
+static uint64_t RoundUp(double d, uint64_t block_size) {
+    uint64_t ret = ((uint64_t)ceil(d) + block_size - 1) / block_size * block_size;
+    CHECK(ret >= d) << "Can't round " << d << " up to a multiple of " << block_size;
+    return ret;
+}
+
+// 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;
+}
+
+std::optional<uint64_t> PartitionCowCreator::GetCowSize(uint64_t snapshot_size) {
+    // TODO: Use |operations|. to determine a minimum COW size.
+    // kCowEstimateFactor is good for prototyping but we can't use that in production.
+    static constexpr double kCowEstimateFactor = 1.05;
+    auto cow_size = RoundUp(snapshot_size * kCowEstimateFactor, kDefaultBlockSize);
+    return cow_size;
+}
+
+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);
+
+    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());
+
+    // TODO(b/141889746): Optimize by using a smaller snapshot. Some ranges in target_partition
+    // may be written directly.
+    ret.snapshot_status.set_snapshot_size(target_partition->size());
+
+    auto cow_size = GetCowSize(ret.snapshot_status.snapshot_size());
+    if (!cow_size.has_value()) return std::nullopt;
+
+    // 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() * kSectorSize;
+    }
+
+    LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
+
+    // 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);
+
+    // The rest of the COW space is allocated on ImageManager.
+    uint64_t cow_file_size = (*cow_size) - ret.snapshot_status.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
new file mode 100644
index 0000000..8888f78
--- /dev/null
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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);
+    std::optional<uint64_t> GetCowSize(uint64_t snapshot_size);
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
new file mode 100644
index 0000000..f683f5b
--- /dev/null
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -0,0 +1,130 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES 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 <liblp/builder.h>
+#include <liblp/property_fetcher.h>
+
+#include "dm_snapshot_internals.h"
+#include "partition_cow_creator.h"
+#include "test_helpers.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(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
index f00129a..48a94e4 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -15,6 +15,7 @@
 #include <libsnapshot/snapshot.h>
 
 #include <dirent.h>
+#include <math.h>
 #include <sys/file.h>
 #include <sys/types.h>
 #include <sys/unistd.h>
@@ -28,14 +29,21 @@
 #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 {
@@ -53,15 +61,20 @@
 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;
 
-// Unit is sectors, this is a 4K chunk.
-static constexpr uint32_t kSnapshotChunkSize = 8;
 static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
 
 class DeviceInfo final : public SnapshotManager::IDeviceInfo {
@@ -69,15 +82,45 @@
     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;
 
   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() {}
@@ -102,7 +145,7 @@
     metadata_dir_ = device_->GetMetadataDir();
 }
 
-[[maybe_unused]] static std::string GetCowName(const std::string& snapshot_name) {
+static std::string GetCowName(const std::string& snapshot_name) {
     return snapshot_name + "-cow";
 }
 
@@ -119,6 +162,16 @@
 }
 
 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;
 
@@ -131,16 +184,45 @@
 }
 
 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(ERROR) << "Cannot cancel update after it has completed or started merging";
-        return false;
+
+    if (state == UpdateState::Initiated) {
+        LOG(INFO) << "Update has been initiated, now canceling";
+        return RemoveAllUpdateState(file.get());
     }
-    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) {
@@ -183,35 +265,50 @@
     return WriteUpdateState(lock.get(), UpdateState::Unverified);
 }
 
-bool SnapshotManager::CreateSnapshot(LockedFile* lock, const std::string& name,
-                                     SnapshotManager::SnapshotStatus status) {
+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 size, however,
+    // 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 " << name
-                   << " device size is not a multiple of the sector size: " << status.device_size;
+    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 " << name << " snapshot size is not a multiple of the sector size: "
-                   << status.snapshot_size;
+    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;
     }
 
-    // Round the COW size up to the nearest sector.
-    status.cow_file_size += kSectorSize - 1;
-    status.cow_file_size &= ~(kSectorSize - 1);
+    status->set_state(SnapshotState::CREATED);
+    status->set_sectors_allocated(0);
+    status->set_metadata_sectors(0);
 
-    status.state = SnapshotState::Created;
-    status.sectors_allocated = 0;
-    status.metadata_sectors = 0;
-
-    if (!WriteSnapshotStatus(lock, name, status)) {
-        PLOG(ERROR) << "Could not write snapshot status: " << name;
+    if (!WriteSnapshotStatus(lock, *status)) {
+        PLOG(ERROR) << "Could not write snapshot status: " << status->name();
         return false;
     }
     return true;
@@ -229,34 +326,15 @@
 
     // 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) {
+    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;
+                   << status.cow_file_size();
         return false;
     }
 
     std::string cow_image_name = GetCowImageDeviceName(name);
     int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;
-    if (!images_->CreateBackingImage(cow_image_name, status.cow_file_size, cow_flags)) {
-        return false;
-    }
-
-    // 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 32 bits. This is a temporary
-    // workaround that will be discussed again when the kernel API gets
-    // consolidated.
-    ssize_t dm_snap_magic_size = 4;  // 32 bit
-    return images_->ZeroFillNewImage(cow_image_name, dm_snap_magic_size);
+    return images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags);
 }
 
 bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
@@ -270,7 +348,7 @@
     if (!ReadSnapshotStatus(lock, name, &status)) {
         return false;
     }
-    if (status.state == SnapshotState::MergeCompleted) {
+    if (status.state() == SnapshotState::MERGE_COMPLETED) {
         LOG(ERROR) << "Should not create a snapshot device for " << name
                    << " after merging has completed.";
         return false;
@@ -289,24 +367,23 @@
             PLOG(ERROR) << "Could not determine block device size: " << base_device;
             return false;
         }
-        if (status.device_size != dev_size) {
+        if (status.device_size() != dev_size) {
             LOG(ERROR) << "Block device size for " << base_device << " does not match"
-                       << "(expected " << status.device_size << ", got " << dev_size << ")";
+                       << "(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;
+    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;
+    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;
-
-
+    uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
+    uint64_t linear_sectors = (status.device_size() - status.snapshot_size()) / kSectorSize;
 
     auto& dm = DeviceMapper::Instance();
 
@@ -346,12 +423,18 @@
     }
 
     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, *dev_path, 0);
+        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)) {
@@ -368,21 +451,24 @@
 }
 
 bool SnapshotManager::MapCowImage(const std::string& name,
-                                  const std::chrono::milliseconds& timeout_ms,
-                                  std::string* cow_dev) {
+                                  const std::chrono::milliseconds& timeout_ms) {
     if (!EnsureImageManager()) return false;
     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);
+        ok = images_->MapImageWithDeviceMapper(opener, cow_image_name, &cow_dev);
     } else {
-        ok = images_->MapImageDevice(cow_image_name, timeout_ms, cow_dev);
+        ok = images_->MapImageDevice(cow_image_name, timeout_ms, &cow_dev);
     }
-    if (!ok) {
+
+    if (ok) {
+        LOG(INFO) << "Mapped " << cow_image_name << " to " << cow_dev;
+    } else {
         LOG(ERROR) << "Could not map image device: " << cow_image_name;
     }
     return ok;
@@ -391,22 +477,15 @@
 bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
     CHECK(lock);
 
-    SnapshotStatus status;
-    if (!ReadSnapshotStatus(lock, name, &status)) {
-        return false;
-    }
-
     auto& dm = DeviceMapper::Instance();
     if (!dm.DeleteDeviceIfExists(name)) {
         LOG(ERROR) << "Could not delete snapshot device: " << name;
         return false;
     }
 
-    // There may be an extra device, since the kernel doesn't let us have a
-    // snapshot and linear target in the same table.
-    auto dm_name = GetSnapshotDeviceName(name, status);
-    if (name != dm_name && !dm.DeleteDeviceIfExists(dm_name)) {
-        LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
+    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;
     }
 
@@ -423,11 +502,12 @@
     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_->UnmapImageIfExists(cow_image_name)) {
-            return false;
-        }
         if (!images_->DeleteBackingImage(cow_image_name)) {
             return false;
         }
@@ -515,8 +595,9 @@
     if (!ReadSnapshotStatus(lock, name, &status)) {
         return false;
     }
-    if (status.state != SnapshotState::Created) {
-        LOG(WARNING) << "Snapshot " << name << " has unexpected state: " << to_string(status.state);
+    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
@@ -526,15 +607,15 @@
         return false;
     }
 
-    status.state = SnapshotState::Merging;
+    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.sectors_allocated = dm_status.sectors_allocated;
-    status.metadata_sectors = dm_status.metadata_sectors;
-    if (!WriteSnapshotStatus(lock, name, status)) {
+    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;
@@ -779,7 +860,7 @@
         // 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::MergeCompleted) {
+        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;
@@ -807,7 +888,7 @@
 
     // These two values are equal when merging is complete.
     if (status.sectors_allocated != status.metadata_sectors) {
-        if (snapshot_status.state == SnapshotState::MergeCompleted) {
+        if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
             LOG(ERROR) << "Snapshot " << name << " is merging after being marked merge-complete.";
             return UpdateState::MergeFailed;
         }
@@ -822,8 +903,8 @@
     // 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.state = SnapshotState::MergeCompleted;
-    if (!WriteSnapshotStatus(lock, name, snapshot_status)) {
+    snapshot_status.set_state(SnapshotState::MERGE_COMPLETED);
+    if (!WriteSnapshotStatus(lock, snapshot_status)) {
         return UpdateState::MergeFailed;
     }
     if (!OnSnapshotMergeComplete(lock, name, snapshot_status)) {
@@ -927,10 +1008,10 @@
         return false;
     }
 
-    uint64_t num_sectors = status.snapshot_size / kSectorSize;
-    if (num_sectors * kSectorSize != status.snapshot_size) {
+    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;
+                   << " size is not sector aligned: " << status.snapshot_size();
         return false;
     }
 
@@ -956,32 +1037,30 @@
                 return false;
             }
         }
-        uint64_t sectors = outer_table[0].spec.length + outer_table[1].spec.length;
-        if (sectors != num_sectors) {
-            LOG(ERROR) << "Outer snapshot " << name << " should have " << num_sectors
-                       << ", got: " << sectors;
+        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;
         }
     }
 
-    // Grab the partition metadata for the snapshot.
     uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
-    auto super_device = device_->GetSuperDevice(slot);
-    const auto& opener = device_->GetPartitionOpener();
-    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
-    if (!metadata) {
-        LOG(ERROR) << "Could not read super partition metadata.";
-        return false;
-    }
-    auto partition = android::fs_mgr::FindPartition(*metadata.get(), name);
-    if (!partition) {
-        LOG(ERROR) << "Snapshot does not have a partition in super: " << name;
-        return false;
-    }
-
     // 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(opener, *metadata.get(), *partition, super_device, &table)) {
+    if (!CreateDmTable(base_device_params, &table)) {
         LOG(ERROR) << "Could not create a DmTable for partition: " << name;
         return false;
     }
@@ -1058,7 +1137,7 @@
 
     bool ok = true;
     for (const auto& name : snapshots) {
-        ok &= DeleteSnapshot(lock, name);
+        ok &= (UnmapPartitionWithSnapshot(lock, name) && DeleteSnapshot(lock, name));
     }
     return ok;
 }
@@ -1160,6 +1239,12 @@
     }
 
     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(),
@@ -1201,6 +1286,12 @@
     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;
@@ -1237,7 +1328,7 @@
             return false;
         }
         // No live snapshot if merge is completed.
-        if (live_snapshot_status->state == SnapshotState::MergeCompleted) {
+        if (live_snapshot_status->state() == SnapshotState::MERGE_COMPLETED) {
             live_snapshot_status.reset();
         }
     } while (0);
@@ -1255,8 +1346,8 @@
     // device itself. This device consists of the real blocks in the super
     // partition that this logical partition occupies.
     auto& dm = DeviceMapper::Instance();
-    std::string ignore_path;
-    if (!CreateLogicalPartition(params, &ignore_path)) {
+    std::string base_path;
+    if (!CreateLogicalPartition(params, &base_path)) {
         LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
                    << " as device " << params.GetDeviceName();
         return false;
@@ -1264,6 +1355,7 @@
     created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
 
     if (!live_snapshot_status.has_value()) {
+        *path = base_path;
         created_devices.Release();
         return true;
     }
@@ -1276,22 +1368,20 @@
         return false;
     }
 
-    // If there is a timeout specified, compute the remaining time to call Map* functions.
-    // init calls CreateLogicalAndSnapshotPartitions, which has no timeout specified. Still call
-    // Map* functions in this case.
     auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
     if (remaining_time.count() < 0) return false;
 
-    std::string cow_image_device;
-    if (!MapCowImage(params.GetPartitionName(), remaining_time, &cow_image_device)) {
-        LOG(ERROR) << "Could not map cow image for partition: " << params.GetPartitionName();
+    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;
     }
-    created_devices.EmplaceBack<AutoUnmapImage>(images_.get(),
-                                                GetCowImageDeviceName(params.partition_name));
-
-    // TODO: map cow linear device here
-    std::string cow_device = cow_image_device;
+    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;
@@ -1310,6 +1400,121 @@
     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);
+    if (!EnsureImageManager()) return false;
+    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) {
+        auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
+        if (remaining_time.count() < 0) return false;
+
+        if (!MapCowImage(partition_name, remaining_time)) {
+            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));
@@ -1317,7 +1522,7 @@
         PLOG(ERROR) << "Open failed: " << file;
         return nullptr;
     }
-    if (flock(fd, lock_flags) < 0) {
+    if (lock_flags != 0 && flock(fd, lock_flags) < 0) {
         PLOG(ERROR) << "Acquire flock failed: " << file;
         return nullptr;
     }
@@ -1384,40 +1589,68 @@
     }
 }
 
-bool SnapshotManager::WriteUpdateState(LockedFile* file, UpdateState state) {
-    std::string contents;
+std::ostream& operator<<(std::ostream& os, UpdateState state) {
     switch (state) {
         case UpdateState::None:
-            contents = "none";
-            break;
+            return os << "none";
         case UpdateState::Initiated:
-            contents = "initiated";
-            break;
+            return os << "initiated";
         case UpdateState::Unverified:
-            contents = "unverified";
-            break;
+            return os << "unverified";
         case UpdateState::Merging:
-            contents = "merging";
-            break;
+            return os << "merging";
         case UpdateState::MergeCompleted:
-            contents = "merge-completed";
-            break;
+            return os << "merge-completed";
         case UpdateState::MergeNeedsReboot:
-            contents = "merge-needs-reboot";
-            break;
+            return os << "merge-needs-reboot";
         case UpdateState::MergeFailed:
-            contents = "merge-failed";
-            break;
+            return os << "merge-failed";
         default:
             LOG(ERROR) << "Unknown update state";
-            return false;
+            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;
 }
 
@@ -1437,101 +1670,38 @@
         return false;
     }
 
-    std::string contents;
-    if (!android::base::ReadFdToString(fd, &contents)) {
-        PLOG(ERROR) << "read failed: " << path;
-        return false;
-    }
-    auto pieces = android::base::Split(contents, " ");
-    if (pieces.size() != 7) {
-        LOG(ERROR) << "Invalid status line for snapshot: " << path;
+    if (!status->ParseFromFileDescriptor(fd.get())) {
+        PLOG(ERROR) << "Unable to parse " << path << " as SnapshotStatus";
         return false;
     }
 
-    if (pieces[0] == "none") {
-        status->state = SnapshotState::None;
-    } else if (pieces[0] == "created") {
-        status->state = SnapshotState::Created;
-    } else if (pieces[0] == "merging") {
-        status->state = SnapshotState::Merging;
-    } else if (pieces[0] == "merge-completed") {
-        status->state = SnapshotState::MergeCompleted;
-    } else {
-        LOG(ERROR) << "Unrecognized state " << pieces[0] << " for snapshot: " << name;
-        return false;
+    if (status->name() != name) {
+        LOG(WARNING) << "Found snapshot status named " << status->name() << " in " << path;
+        status->set_name(name);
     }
 
-    if (!android::base::ParseUint(pieces[1], &status->device_size)) {
-        LOG(ERROR) << "Invalid device size in status line for: " << path;
-        return false;
-    }
-    if (!android::base::ParseUint(pieces[2], &status->snapshot_size)) {
-        LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
-        return false;
-    }
-    if (!android::base::ParseUint(pieces[3], &status->cow_partition_size)) {
-        LOG(ERROR) << "Invalid cow linear size in status line for: " << path;
-        return false;
-    }
-    if (!android::base::ParseUint(pieces[4], &status->cow_file_size)) {
-        LOG(ERROR) << "Invalid cow file size in status line for: " << path;
-        return false;
-    }
-    if (!android::base::ParseUint(pieces[5], &status->sectors_allocated)) {
-        LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
-        return false;
-    }
-    if (!android::base::ParseUint(pieces[6], &status->metadata_sectors)) {
-        LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
-        return false;
-    }
     return true;
 }
 
-std::string SnapshotManager::to_string(SnapshotState state) {
-    switch (state) {
-        case SnapshotState::None:
-            return "none";
-        case SnapshotState::Created:
-            return "created";
-        case SnapshotState::Merging:
-            return "merging";
-        case SnapshotState::MergeCompleted:
-            return "merge-completed";
-        default:
-            LOG(ERROR) << "Unknown snapshot state: " << (int)state;
-            return "unknown";
-    }
-}
-
-bool SnapshotManager::WriteSnapshotStatus(LockedFile* lock, const std::string& name,
-                                          const SnapshotStatus& status) {
+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(name);
-    unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW | O_CREAT | O_SYNC, 0660));
+    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;
     }
 
-    std::vector<std::string> pieces = {
-            to_string(status.state),
-            std::to_string(status.device_size),
-            std::to_string(status.snapshot_size),
-            std::to_string(status.cow_partition_size),
-            std::to_string(status.cow_file_size),
-            std::to_string(status.sectors_allocated),
-            std::to_string(status.metadata_sectors),
-    };
-    auto contents = android::base::Join(pieces, " ");
-
-    if (!android::base::WriteStringToFd(contents, fd)) {
-        PLOG(ERROR) << "write failed: " << path;
+    if (!status.SerializeToFileDescriptor(fd.get())) {
+        PLOG(ERROR) << "Unable to write SnapshotStatus to " << path;
         return false;
     }
+
     return true;
 }
 
@@ -1549,7 +1719,7 @@
 
 std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
                                                    const SnapshotStatus& status) {
-    if (status.device_size != status.snapshot_size) {
+    if (status.device_size() != status.snapshot_size()) {
         return GetSnapshotExtraDeviceName(snapshot_name);
     }
     return snapshot_name;
@@ -1577,5 +1747,324 @@
     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);
+
+    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;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
new file mode 100644
index 0000000..60bf796
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
@@ -0,0 +1,273 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "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
new file mode 100644
index 0000000..83c9460
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater.h
@@ -0,0 +1,85 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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
new file mode 100644
index 0000000..7d96a67
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -0,0 +1,331 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES 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(, 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
index 429fd8e..a008294 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -31,9 +31,11 @@
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
 #include <liblp/builder.h>
-#include <liblp/mock_property_fetcher.h>
+#include <storage_literals/storage_literals.h>
 
+#include <android/snapshot/snapshot.pb.h>
 #include "test_helpers.h"
+#include "utility.h"
 
 namespace android {
 namespace snapshot {
@@ -45,21 +47,23 @@
 using android::fs_mgr::BlockDeviceInfo;
 using android::fs_mgr::CreateLogicalPartitionParams;
 using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::Extent;
+using android::fs_mgr::GetPartitionGroupName;
 using android::fs_mgr::GetPartitionName;
+using android::fs_mgr::Interval;
 using android::fs_mgr::MetadataBuilder;
+using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::PartitionUpdate;
 using namespace ::testing;
-using namespace android::fs_mgr::testing;
+using namespace android::storage_literals;
 using namespace std::chrono_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).
+// Global states. See test_helpers.h.
 std::unique_ptr<SnapshotManager> sm;
 TestDeviceInfo* test_device = nullptr;
 std::string fake_super;
 
-static constexpr uint64_t kSuperSize = 16 * 1024 * 1024;
-
 class SnapshotTest : public ::testing::Test {
   public:
     SnapshotTest() : dm_(DeviceMapper::Instance()) {}
@@ -72,7 +76,7 @@
 
   protected:
     void SetUp() override {
-        ResetMockPropertyFetcher();
+        SnapshotTestPropertyFetcher::SetUp();
         InitializeState();
         CleanupTestArtifacts();
         FormatFakeSuper();
@@ -84,7 +88,7 @@
         lock_ = nullptr;
 
         CleanupTestArtifacts();
-        ResetMockPropertyFetcher();
+        SnapshotTestPropertyFetcher::TearDown();
     }
 
     void InitializeState() {
@@ -105,7 +109,7 @@
         std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
                                               "test_partition_b"};
         for (const auto& snapshot : snapshots) {
-            DeleteSnapshotDevice(snapshot);
+            ASSERT_TRUE(DeleteSnapshotDevice(snapshot));
             DeleteBackingImage(image_manager_, snapshot + "-cow-img");
 
             auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
@@ -199,9 +203,9 @@
                     .block_device = fake_super,
                     .metadata = metadata.get(),
                     .partition = &partition,
-                    .device_name = GetPartitionName(partition) + "-base",
                     .force_writable = true,
                     .timeout_ms = 10s,
+                    .device_name = GetPartitionName(partition) + "-base",
             };
             std::string ignore_path;
             if (!CreateLogicalPartition(params, &ignore_path)) {
@@ -211,15 +215,52 @@
         return true;
     }
 
-    void DeleteSnapshotDevice(const std::string& snapshot) {
-        DeleteDevice(snapshot);
-        DeleteDevice(snapshot + "-inner");
-        ASSERT_TRUE(image_manager_->UnmapImageIfExists(snapshot + "-cow-img"));
-    }
-    void DeleteDevice(const std::string& device) {
-        if (dm_.GetState(device) != DmDeviceState::INVALID) {
-            ASSERT_TRUE(dm_.DeleteDevice(device));
+    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) {
+        if (!sm->MapCowImage(name, timeout_ms)) {
+            return AssertionFailure() << "Cannot map cow image " << name;
+        }
+        if (!dm_.GetDmDevicePathByName(name + "-cow-img"s, path)) {
+            return AssertionFailure() << "No path for " << name << "-cow-img";
+        }
+        return AssertionSuccess();
     }
 
     DeviceMapper& dm_;
@@ -232,11 +273,13 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
-                                   {.device_size = kDeviceSize,
-                                    .snapshot_size = kDeviceSize,
-                                    .cow_file_size = kDeviceSize}));
-    ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
+    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));
@@ -245,11 +288,11 @@
 
     // Scope so delete can re-acquire the snapshot file lock.
     {
-        SnapshotManager::SnapshotStatus status;
+        SnapshotStatus status;
         ASSERT_TRUE(sm->ReadSnapshotStatus(lock_.get(), "test-snapshot", &status));
-        ASSERT_EQ(status.state, SnapshotManager::SnapshotState::Created);
-        ASSERT_EQ(status.device_size, kDeviceSize);
-        ASSERT_EQ(status.snapshot_size, kDeviceSize);
+        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"));
@@ -261,17 +304,19 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
-                                   {.device_size = kDeviceSize,
-                                    .snapshot_size = kDeviceSize,
-                                    .cow_file_size = kDeviceSize}));
-    ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
+    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(sm->MapCowImage("test-snapshot", 10s, &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,
@@ -284,17 +329,19 @@
 
     static const uint64_t kSnapshotSize = 1024 * 1024;
     static const uint64_t kDeviceSize = 1024 * 1024 * 2;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
-                                   {.device_size = kDeviceSize,
-                                    .snapshot_size = kSnapshotSize,
-                                    .cow_file_size = kSnapshotSize}));
-    ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
+    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(sm->MapCowImage("test-snapshot", 10s, &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,
@@ -329,9 +376,6 @@
 }
 
 TEST_F(SnapshotTest, Merge) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -340,12 +384,14 @@
     ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
     ASSERT_TRUE(MapUpdatePartitions());
     ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b-base", &base_device));
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
-                                   {.device_size = kDeviceSize,
-                                    .snapshot_size = kDeviceSize,
-                                    .cow_file_size = kDeviceSize}));
-    ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
-    ASSERT_TRUE(sm->MapCowImage("test_partition_b", 10s, &cow_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));
 
@@ -399,15 +445,17 @@
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot",
-                                   {.device_size = kDeviceSize,
-                                    .snapshot_size = kDeviceSize,
-                                    .cow_file_size = kDeviceSize}));
-    ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test-snapshot"));
+    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, cow_device, snap_device;
     ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
-    ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
     ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
                                 &snap_device));
 
@@ -433,11 +481,11 @@
     // Wait 1s, otherwise DeleteSnapshotDevice may fail with EBUSY.
     sleep(1);
     // Forcefully delete the snapshot device, so it looks like we just rebooted.
-    DeleteSnapshotDevice("test-snapshot");
+    ASSERT_TRUE(DeleteSnapshotDevice("test-snapshot"));
 
     // Map snapshot should fail now, because we're in a merge-complete state.
     ASSERT_TRUE(AcquireLock());
-    ASSERT_TRUE(sm->MapCowImage("test-snapshot", 10s, &cow_device));
+    ASSERT_TRUE(MapCowImage("test-snapshot", 10s, &cow_device));
     ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, cow_device, 10s,
                                  &snap_device));
 
@@ -449,30 +497,26 @@
 }
 
 TEST_F(SnapshotTest, FirstStageMountAndMerge) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
 
     ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
     ASSERT_TRUE(MapUpdatePartitions());
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
-                                   {.device_size = kDeviceSize,
-                                    .snapshot_size = kDeviceSize,
-                                    .cow_file_size = kDeviceSize}));
-    ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
+    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 rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_b");
-
-    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
@@ -480,9 +524,8 @@
     ASSERT_TRUE(AcquireLock());
 
     // Validate that we have a snapshot device.
-    SnapshotManager::SnapshotStatus status;
     ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
-    ASSERT_EQ(status.state, SnapshotManager::SnapshotState::Created);
+    ASSERT_EQ(status.state(), SnapshotState::CREATED);
 
     DeviceMapper::TargetInfo target;
     auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
@@ -491,20 +534,19 @@
 }
 
 TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
 
     ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
     ASSERT_TRUE(MapUpdatePartitions());
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
-                                   {.device_size = kDeviceSize,
-                                    .snapshot_size = kDeviceSize,
-                                    .cow_file_size = kDeviceSize}));
-    ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
+    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;
@@ -515,17 +557,13 @@
     FormatFakeSuper();
     ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
 
-    auto rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_b");
-
-    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    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());
 
-    SnapshotManager::SnapshotStatus status;
     ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
 
     // We should not get a snapshot device now.
@@ -539,30 +577,26 @@
 }
 
 TEST_F(SnapshotTest, FlashSuperDuringMerge) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
 
     ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize));
     ASSERT_TRUE(MapUpdatePartitions());
-    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b",
-                                   {.device_size = kDeviceSize,
-                                    .snapshot_size = kDeviceSize,
-                                    .cow_file_size = kDeviceSize}));
-    ASSERT_TRUE(sm->CreateCowImage(lock_.get(), "test_partition_b"));
+    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 rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_b");
-
-    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
@@ -570,7 +604,7 @@
 
     // Now, reflash super. Note that we haven't called ProcessUpdateState, so the
     // status is still Merging.
-    DeleteSnapshotDevice("test_partition_b");
+    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));
@@ -583,6 +617,446 @@
     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.
+        auto 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"));
+        ASSERT_NE(nullptr, src_);
+        // Add sys_b which is like system_other.
+        auto partition = src_->AddPartition("sys_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;
+        }
+        if (hashes_[name] != *hash) {
+            return AssertionFailure() << "Content of " << name << " has changed after the merge";
+        }
+        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;
+};
+
+// 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.
+    SetSize(sys_, 3788_KiB);
+    SetSize(vnd_, 3788_KiB);
+    SetSize(prd_, 3788_KiB);
+
+    // 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_TRUE(init->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
+
+    // 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"));
+
+    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_TRUE(new_sm->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
+
+    // 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";
+        }
+    }
+}
+
 }  // namespace snapshot
 }  // namespace android
 
@@ -624,6 +1098,7 @@
     }
 
     // Clean up previous run.
+    SnapshotUpdateTest().Cleanup();
     SnapshotTest().Cleanup();
 
     // Use a separate image manager for our fake super partition.
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
new file mode 100644
index 0000000..d65320c
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -0,0 +1,115 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES 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 sm = SnapshotManager::New();
+
+    auto state = sm->GetUpdateState();
+    if (state == UpdateState::None) {
+        LOG(INFO) << "Can't find any snapshot to merge.";
+        return true;
+    }
+    if (state == UpdateState::Unverified) {
+        if (!sm->InitiateMerge()) {
+            LOG(ERROR) << "Failed to initiate merge.";
+            return false;
+        }
+    }
+
+    // All other states can be handled by ProcessUpdateState.
+    LOG(INFO) << "Waiting for any merge to complete. This can take up to 1 minute.";
+    state = SnapshotManager::New()->ProcessUpdateState();
+
+    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
new file mode 100644
index 0000000..29707f1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshotctl.rc
@@ -0,0 +1,2 @@
+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
index f67dd21..539c5c5 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -14,12 +14,21 @@
 
 #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)) {
@@ -53,5 +62,83 @@
     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);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index 9f582d9..ea2c5b6 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -14,17 +14,44 @@
 
 #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:
@@ -44,26 +71,72 @@
   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; }
 
     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);
     }
+    MergeStatus merge_status() const { return merge_status_; }
 
   private:
     std::string slot_suffix_ = "_a";
     std::unique_ptr<TestPartitionOpener> opener_;
+    MergeStatus merge_status_;
+};
+
+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);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 164b472..66629e8 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -14,9 +14,13 @@
 
 #include "utility.h"
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 
+using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::Partition;
+
 namespace android {
 namespace snapshot {
 
@@ -52,5 +56,58 @@
     }
 }
 
+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 32 bits. This is a temporary
+    // workaround that will be discussed again when the kernel API gets
+    // consolidated.
+    // TODO(b/139202197): Remove this hack once the kernel API is consolidated.
+    constexpr ssize_t kDmSnapZeroFillSize = 4;  // 32-bit
+
+    char 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, kDmSnapZeroFillSize)) {
+        PLOG(ERROR) << "Can't zero-fill COW device for " << device;
+        return false;
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index cbab472..3051184 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -14,15 +14,22 @@
 
 #pragma once
 
+#include <functional>
 #include <string>
 
 #include <android-base/macros.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>
 
 namespace android {
 namespace snapshot {
 
+// Unit is sectors, this is a 4K chunk.
+static constexpr uint32_t kSnapshotChunkSize = 8;
+
 struct AutoDevice {
     virtual ~AutoDevice(){};
     void Release();
@@ -81,5 +88,27 @@
     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;
+};
+
+// 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
new file mode 100644
index 0000000..11611dd
--- /dev/null
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -0,0 +1,6 @@
+
+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
new file mode 100644
index 0000000..ac0dfbd
--- /dev/null
+++ b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#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
new file mode 100644
index 0000000..937e0f3
--- /dev/null
+++ b/fs_mgr/libvbmeta/Android.bp
@@ -0,0 +1,52 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+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
new file mode 100644
index 0000000..a901a4f
--- /dev/null
+++ b/fs_mgr/libvbmeta/builder.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
new file mode 100644
index 0000000..58ea36a
--- /dev/null
+++ b/fs_mgr/libvbmeta/builder.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
new file mode 100644
index 0000000..9a015fd
--- /dev/null
+++ b/fs_mgr/libvbmeta/builder_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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/libnativeloader/utils.h b/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
similarity index 69%
rename from libnativeloader/utils.h
rename to fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
index a1c2be5..ab7ba73 100644
--- a/libnativeloader/utils.h
+++ b/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
@@ -13,14 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-namespace android::nativeloader {
+#include <map>
+#include <string>
 
-#if defined(__LP64__)
-#define LIB "lib64"
-#else
-#define LIB "lib"
-#endif
+namespace android {
+namespace fs_mgr {
 
-}  // namespace android::nativeloader
+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
diff --git a/fs_mgr/libvbmeta/reader.cpp b/fs_mgr/libvbmeta/reader.cpp
new file mode 100644
index 0000000..212d186
--- /dev/null
+++ b/fs_mgr/libvbmeta/reader.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
new file mode 100644
index 0000000..f0997ff
--- /dev/null
+++ b/fs_mgr/libvbmeta/reader.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <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/libnativeloader/utils.h b/fs_mgr/libvbmeta/super_vbmeta_format.h
similarity index 60%
copy from libnativeloader/utils.h
copy to fs_mgr/libvbmeta/super_vbmeta_format.h
index a1c2be5..c62a2dd 100644
--- a/libnativeloader/utils.h
+++ b/fs_mgr/libvbmeta/super_vbmeta_format.h
@@ -13,14 +13,22 @@
  * 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
 
-namespace android::nativeloader {
+#include <string>
+#include <vector>
 
-#if defined(__LP64__)
-#define LIB "lib64"
-#else
-#define LIB "lib"
-#endif
+#include "super_vbmeta_format_c.h"
 
-}  // namespace android::nativeloader
+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
new file mode 100644
index 0000000..6d79801
--- /dev/null
+++ b/fs_mgr/libvbmeta/super_vbmeta_format_c.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
new file mode 100644
index 0000000..6b4fc5d
--- /dev/null
+++ b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
new file mode 100644
index 0000000..c184227
--- /dev/null
+++ b/fs_mgr/libvbmeta/utility.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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
new file mode 100644
index 0000000..91db0ad
--- /dev/null
+++ b/fs_mgr/libvbmeta/utility.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <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
new file mode 100644
index 0000000..fc0e0fb
--- /dev/null
+++ b/fs_mgr/libvbmeta/writer.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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
new file mode 100644
index 0000000..f8eed36
--- /dev/null
+++ b/fs_mgr/libvbmeta/writer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <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/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index f445703..4226e95 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -38,6 +38,8 @@
 
 EMPTY=""
 SPACE=" "
+# Line up wrap to [  XXXXXXX ] messages.
+INDENT="             "
 # A _real_ embedded tab character
 TAB="`echo | tr '\n' '\t'`"
 # A _real_ embedded escape character
@@ -159,8 +161,7 @@
     return
   fi
   echo "${ORANGE}[  WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
-  echo "${L}" |
-    sed 's/^/             /' >&2
+  echo "${L}" | sed "s/^/${INDENT}/" >&2
 }
 
 [ "USAGE: get_property <prop>
@@ -639,10 +640,10 @@
 *}" ]; then
       echo "${prefix} expected \"${lval}\""
       echo "${prefix} got \"${rval}\"" |
-        sed ': again
+        sed ": again
              N
-             s/\(\n\)\([^ ]\)/\1             \2/
-             t again'
+             s/\(\n\)\([^ ]\)/\1${INDENT}\2/
+             t again"
       if [ -n "${*}" ] ; then
         echo "${prefix} ${*}"
       fi
@@ -657,10 +658,10 @@
       if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
         echo "${prefix} ok \"${lval}\""
         echo "       = \"${rval}\"" |
-          sed ': again
+          sed ": again
                N
-               s/\(\n\)\([^ ]\)/\1          \2/
-               t again'
+               s/\(\n\)\([^ ]\)/\1${INDENT}\2/
+               t again"
         if [ -n "${*}" ] ; then
           echo "${prefix} ${*}"
         fi
@@ -955,13 +956,24 @@
   echo "${GREEN}[ RUN      ]${NORMAL} Testing adb shell su root remount -R command" >&2
 
   avc_check
-  adb_su remount -R system </dev/null || true
+  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
   sleep 2
   adb_wait ${ADB_WAIT} ||
-    die "waiting for device after remount -R `usb_status`"
+    die "waiting for device after adb shell su root remount -R system `usb_status`"
   if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
        "2" = "`get_property partition.system.verified`" ]; then
-    die "remount -R command failed"
+    die ${T} "remount -R command failed
+${INDENT}ro.boot.verifiedbootstate=\"`get_property ro.boot.verifiedbootstate`\"
+${INDENT}partition.system.verified=\"`get_property partition.system.verified`\""
   fi
 
   echo "${GREEN}[       OK ]${NORMAL} adb shell su root remount -R command" >&2
@@ -1643,15 +1655,24 @@
 if ${overlayfs_supported}; then
   echo "${GREEN}[ RUN      ]${NORMAL} test 'adb remount -R'" >&2
   avc_check
-  adb_root &&
-    adb remount -R &&
-    adb_wait ${ADB_WAIT} ||
-    die "adb remount -R"
+  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`"
   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"
+    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`\""
   fi
 
   echo "${GREEN}[       OK ]${NORMAL} 'adb remount -R' command" >&2
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 6d87594..a7ea817 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -467,6 +467,7 @@
     }
     EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
     EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+    EXPECT_EQ(1, entry->file_policy_version);
     EXPECT_EQ("", entry->key_loc);
 }
 
@@ -682,6 +683,7 @@
     EXPECT_EQ("/dir/key", entry->key_loc);
     EXPECT_EQ("aes-256-xts", entry->file_contents_mode);
     EXPECT_EQ("aes-256-cts", entry->file_names_mode);
+    EXPECT_EQ(1, entry->file_policy_version);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {
@@ -698,14 +700,18 @@
 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:
+source none10      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:v1
+source none11      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:v2
+source none12      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:v2:
+source none13      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:blah
+source none14      swap   defaults      fileencryption=aes-256-xts:aes-256-cts:vblah
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
-    ASSERT_EQ(11U, fstab.size());
+    ASSERT_EQ(15U, fstab.size());
 
     FstabEntry::FsMgrFlags flags = {};
     flags.file_encryption = true;
@@ -715,66 +721,105 @@
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     EXPECT_EQ("", entry->file_contents_mode);
     EXPECT_EQ("", entry->file_names_mode);
+    EXPECT_EQ(0, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(1, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(1, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(1, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(1, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(1, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(0, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(1, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(1, entry->file_policy_version);
 
     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);
+    EXPECT_EQ(1, entry->file_policy_version);
 
     entry++;
     EXPECT_EQ("none10", 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);
+    EXPECT_EQ(1, entry->file_policy_version);
+
+    entry++;
+    EXPECT_EQ("none11", 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);
+    EXPECT_EQ(2, entry->file_policy_version);
+
+    entry++;
+    EXPECT_EQ("none12", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     EXPECT_EQ("", entry->file_contents_mode);
     EXPECT_EQ("", entry->file_names_mode);
+    EXPECT_EQ(0, entry->file_policy_version);
+
+    entry++;
+    EXPECT_EQ("none13", 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);
+    EXPECT_EQ(0, entry->file_policy_version);
+
+    entry++;
+    EXPECT_EQ("none14", 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);
+    EXPECT_EQ(0, entry->file_policy_version);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MaxCompStreams) {
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 06c8176..57ed362 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -200,9 +200,7 @@
     return value;
 }
 
-bool BatteryMonitor::update(void) {
-    bool logthis;
-
+void BatteryMonitor::updateValues(void) {
     initBatteryProperties(&props);
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
@@ -289,50 +287,44 @@
             }
         }
     }
+}
 
-    logthis = !healthd_board_battery_update(&props);
+void BatteryMonitor::logValues(void) {
+    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);
 
-    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");
+        len = strlen(dmesgline);
+        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
+                            props.batteryCurrent);
         }
 
-        snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
-                 props.chargerAcOnline ? "a" : "",
-                 props.chargerUsbOnline ? "u" : "",
-                 props.chargerWirelessOnline ? "w" : "");
+        if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
+                            props.batteryFullCharge);
+        }
 
-        KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
+        if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
+                            props.batteryCycleCount);
+        }
+    } else {
+        len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
     }
 
-    healthd_mode_ops->battery_update(&props);
+    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() {
     return props.chargerAcOnline | props.chargerUsbOnline |
             props.chargerWirelessOnline;
 }
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 4d1d53f..0fd3824 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -38,12 +38,15 @@
 
     BatteryMonitor();
     void init(struct healthd_config *hc);
-    bool update(void);
     int getChargeStatus();
     status_t getProperty(int id, struct BatteryProperty *val);
     void dumpState(int fd);
     friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
 
+    void updateValues(void);
+    void logValues(void);
+    bool isChargerOnline();
+
   private:
     struct healthd_config *mHealthdConfig;
     Vector<String8> mChargerNames;
diff --git a/init/Android.bp b/init/Android.bp
index 57555f6..bd2d38c 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -62,7 +62,6 @@
         },
     },
     static_libs: [
-        "libseccomp_policy",
         "libavb",
         "libc++fs",
         "libcgrouprc_format",
@@ -70,21 +69,19 @@
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
-        "libsnapshot_nobinder",
+        "libsnapshot_init",
     ],
     shared_libs: [
         "libbacktrace",
         "libbase",
         "libbootloader_message",
         "libcutils",
-        "libcrypto",
         "libdl",
         "libext4_utils",
         "libfs_mgr",
         "libfscrypt",
         "libgsi",
         "libhidl-gen-utils",
-        "libjsoncpp",
         "libkeyutils",
         "liblog",
         "liblogwrap",
@@ -109,7 +106,6 @@
         "action.cpp",
         "action_manager.cpp",
         "action_parser.cpp",
-        "boringssl_self_test.cpp",
         "bootchart.cpp",
         "builtins.cpp",
         "capabilities.cpp",
@@ -130,6 +126,7 @@
         "persistent_properties.cpp",
         "persistent_properties.proto",
         "property_service.cpp",
+        "property_service.proto",
         "property_type.cpp",
         "reboot.cpp",
         "reboot_utils.cpp",
@@ -224,6 +221,7 @@
 
     srcs: [
         "devices_test.cpp",
+        "firmware_handler_test.cpp",
         "init_test.cpp",
         "keychords_test.cpp",
         "persistent_properties_test.cpp",
@@ -287,13 +285,11 @@
     shared_libs: [
         "libcutils",
         "libhidl-gen-utils",
+        "libhidlmetadata",
         "liblog",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
     ],
-    header_libs: [
-        "libjsoncpp_headers",
-    ],
     srcs: [
         "action.cpp",
         "action_manager.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index d7258a7..4e4c002 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -92,7 +92,6 @@
     liblogwrap \
     libext4_utils \
     libfscrypt \
-    libseccomp_policy \
     libcrypto_utils \
     libsparse \
     libavb \
@@ -101,7 +100,7 @@
     libcutils \
     libbase \
     liblog \
-    libcrypto \
+    libcrypto_static \
     libdl \
     libz \
     libselinux \
@@ -109,12 +108,13 @@
     libgsi \
     libcom.android.sysprop.apex \
     liblzma \
-    libdexfile_support \
+    libdexfile_support_static \
     libunwindstack \
     libbacktrace \
     libmodprobe \
     libext2_uuid \
-    libsnapshot_nobinder \
+    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/README.md b/init/README.md
index 2de76a9..cdf3487 100644
--- a/init/README.md
+++ b/init/README.md
@@ -170,6 +170,8 @@
   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
@@ -263,6 +265,12 @@
 > 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,6 +315,13 @@
   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.
@@ -769,6 +784,12 @@
 
 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.
 
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index c592c37..053ebf8 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -82,7 +82,7 @@
 
 ## Firmware loading
 ----------------
-Ueventd automatically serves firmware requests by searching through a list of firmware directories
+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.
 
@@ -100,6 +100,26 @@
 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
@@ -110,3 +130,9 @@
 
 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_parser.cpp b/init/action_parser.cpp
index ff20e43..a8e1e09 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,11 +16,14 @@
 
 #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
@@ -77,6 +80,17 @@
     return {};
 }
 
+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) {
@@ -103,6 +117,9 @@
             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];
         }
@@ -121,13 +138,8 @@
     }
 
     Subcontext* action_subcontext = nullptr;
-    if (subcontexts_) {
-        for (auto& subcontext : *subcontexts_) {
-            if (StartsWith(filename, subcontext.path_prefix())) {
-                action_subcontext = &subcontext;
-                break;
-            }
-        }
+    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
+        action_subcontext = subcontext_;
     }
 
     std::string event_trigger;
diff --git a/init/action_parser.h b/init/action_parser.h
index 2fe9983..3000132 100644
--- a/init/action_parser.h
+++ b/init/action_parser.h
@@ -30,8 +30,8 @@
 
 class ActionParser : public SectionParser {
   public:
-    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
-        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
+    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;
@@ -39,7 +39,7 @@
 
   private:
     ActionManager* action_manager_;
-    std::vector<Subcontext>* subcontexts_;
+    Subcontext* subcontext_;
     std::unique_ptr<Action> action_;
 };
 
diff --git a/init/boringssl_self_test.cpp b/init/boringssl_self_test.cpp
deleted file mode 100644
index 759eb43..0000000
--- a/init/boringssl_self_test.cpp
+++ /dev/null
@@ -1,56 +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 "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<void> 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 {};
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/boringssl_self_test.h b/init/boringssl_self_test.h
deleted file mode 100644
index 9e717d0..0000000
--- a/init/boringssl_self_test.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "builtin_arguments.h"
-#include "result.h"
-
-namespace android {
-namespace init {
-
-Result<void> StartBoringSslSelfTest(const BuiltinArguments&);
-
-}  // namespace init
-}  // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index d2c73cb..2f2ead0 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -80,6 +80,7 @@
 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;
@@ -137,7 +138,14 @@
     if (!write_bootloader_message(options, &err)) {
         return Error() << "Failed to set bootloader message: " << err;
     }
-    property_set("sys.powerctl", "reboot,recovery");
+    // 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 {};
 }
 
@@ -701,6 +709,15 @@
 }
 
 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";
+    }
+
     property_set(args[1], args[2]);
     return {};
 }
@@ -1016,7 +1033,20 @@
 }
 
 static Result<void> do_load_persist_props(const BuiltinArguments& args) {
-    load_persist_props();
+    // 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 {};
 }
 
@@ -1081,20 +1111,6 @@
     return {};
 }
 
-static Result<void> do_exec_reboot_on_failure(const BuiltinArguments& args) {
-    auto reboot_reason = args[1];
-    auto reboot = [reboot_reason](const std::string& message) {
-        property_set(LAST_REBOOT_REASON_PROPERTY, reboot_reason);
-        sync();
-        LOG(FATAL) << message << ": rebooting into bootloader, reason: " << reboot_reason;
-    };
-
-    std::vector<std::string> remaining_args(args.begin() + 1, args.end());
-    remaining_args[0] = args[0];
-
-    return ExecWithFunctionOnFailure(remaining_args, reboot);
-}
-
 static Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {
     auto reboot_reason = vdc_arg + "_failed";
 
@@ -1202,7 +1218,6 @@
         {"enable",                  {1,     1,    {false,  do_enable}}},
         {"exec",                    {1,     kMax, {false,  do_exec}}},
         {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
-        {"exec_reboot_on_failure",  {2,     kMax, {false,  do_exec_reboot_on_failure}}},
         {"exec_start",              {1,     1,    {false,  do_exec_start}}},
         {"export",                  {2,     2,    {false,  do_export}}},
         {"hostname",                {1,     1,    {true,   do_hostname}}},
diff --git a/init/devices.cpp b/init/devices.cpp
index f6e453a..9fbec64 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -441,6 +441,23 @@
     }
 }
 
+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);
@@ -485,6 +502,10 @@
     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 442c53f..05d64da 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -130,6 +130,7 @@
     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/firmware_handler.cpp b/init/firmware_handler.cpp
index c067f6f..1dce2d5 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -17,6 +17,10 @@
 #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>
@@ -26,25 +30,29 @@
 #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 Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
-                         int loading_fd, int data_fd) {
+static void LoadFirmware(const std::string& firmware, 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 << "', '" << uevent.firmware
-                    << "' }";
+        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << firmware << "' }";
     }
 
     // Tell the firmware whether to abort or commit.
@@ -56,36 +64,151 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
-FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories)
-    : firmware_directories_(std::move(firmware_directories)) {}
+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)) {}
 
-void FirmwareHandler::ProcessFirmwareEvent(const Uevent& uevent) {
-    int booting = IsBooting();
+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";
+    }
 
+    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;
+}
 
-    std::string root = "/sys" + uevent.path;
+void FirmwareHandler::ProcessFirmwareEvent(const std::string& root,
+                                           const std::string& firmware) const {
     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 " << uevent.firmware;
+        PLOG(ERROR) << "couldn't open firmware loading fd for " << 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 " << uevent.firmware;
+        PLOG(ERROR) << "couldn't open firmware data fd for " << 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 + uevent.firmware;
+        std::string file = firmware_directory + 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 +
@@ -98,7 +221,7 @@
                                                     ", fstat failed: " + strerror(errno));
             continue;
         }
-        LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+        LoadFirmware(firmware, root, fw_fd, sb.st_size, loading_fd, data_fd);
         return;
     }
 
@@ -110,7 +233,7 @@
         goto try_loading_again;
     }
 
-    LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
+    LOG(ERROR) << "firmware: could not find firmware for " << firmware;
     for (const auto& message : attempted_paths_and_errors) {
         LOG(ERROR) << message;
     }
@@ -129,7 +252,8 @@
     }
     if (pid == 0) {
         Timer t;
-        ProcessFirmwareEvent(uevent);
+        auto firmware = GetFirmwarePath(uevent);
+        ProcessFirmwareEvent("/sys" + uevent.path, firmware);
         LOG(INFO) << "loading " << uevent.path << " took " << t;
         _exit(EXIT_SUCCESS);
     }
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 3996096..b4138f1 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -14,32 +14,48 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_FIRMWARE_HANDLER_H
-#define _INIT_FIRMWARE_HANDLER_H
+#pragma once
+
+#include <pwd.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:
-    explicit FirmwareHandler(std::vector<std::string> firmware_directories);
+    FirmwareHandler(std::vector<std::string> firmware_directories,
+                    std::vector<ExternalFirmwareHandler> external_firmware_handlers);
     virtual ~FirmwareHandler() = default;
 
     void HandleUevent(const Uevent& uevent) override;
 
   private:
-    void ProcessFirmwareEvent(const Uevent& uevent);
+    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;
 
     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
new file mode 100644
index 0000000..7bb603c
--- /dev/null
+++ b/init/firmware_handler_test.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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_mount.cpp b/init/first_stage_mount.cpp
index 6b4216f..9121bac 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -50,12 +50,13 @@
 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;
@@ -77,8 +78,9 @@
     bool InitDevices();
 
   protected:
-    ListenerAction HandleBlockDevice(const std::string& name, const Uevent&);
-    bool InitRequiredDevices();
+    ListenerAction HandleBlockDevice(const std::string& name, const Uevent&,
+                                     std::set<std::string>* required_devices);
+    bool InitRequiredDevices(std::set<std::string> devices);
     bool InitMappedDevice(const std::string& verity_device);
     bool InitDeviceMapper();
     bool CreateLogicalPartitions();
@@ -89,14 +91,14 @@
     bool TrySwitchSystemAsRoot();
     bool TrySkipMountingPartitions();
     bool IsDmLinearEnabled();
-    bool GetDmLinearMetadataDevice();
+    void GetDmLinearMetadataDevice(std::set<std::string>* devices);
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
     void UseGsiIfPresent();
 
-    ListenerAction UeventCallback(const Uevent& uevent);
+    ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);
 
     // Pure virtual functions.
-    virtual bool GetDmVerityDevices() = 0;
+    virtual bool GetDmVerityDevices(std::set<std::string>* devices) = 0;
     virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
 
     bool need_dm_verity_;
@@ -104,7 +106,6 @@
 
     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_;
@@ -116,7 +117,7 @@
     ~FirstStageMountVBootV1() override = default;
 
   protected:
-    bool GetDmVerityDevices() override;
+    bool GetDmVerityDevices(std::set<std::string>* devices) override;
     bool SetUpDmVerity(FstabEntry* fstab_entry) override;
 };
 
@@ -128,7 +129,7 @@
     ~FirstStageMountVBootV2() override = default;
 
   protected:
-    bool GetDmVerityDevices() override;
+    bool GetDmVerityDevices(std::set<std::string>* devices) override;
     bool SetUpDmVerity(FstabEntry* fstab_entry) override;
     bool InitAvbHandle();
 
@@ -252,7 +253,13 @@
 }
 
 bool FirstStageMount::InitDevices() {
-    return GetDmLinearMetadataDevice() && GetDmVerityDevices() && InitRequiredDevices();
+    std::set<std::string> devices;
+    GetDmLinearMetadataDevice(&devices);
+
+    if (!GetDmVerityDevices(&devices)) {
+        return false;
+    }
+    return InitRequiredDevices(std::move(devices));
 }
 
 bool FirstStageMount::IsDmLinearEnabled() {
@@ -262,45 +269,46 @@
     return false;
 }
 
-bool FirstStageMount::GetDmLinearMetadataDevice() {
+void FirstStageMount::GetDmLinearMetadataDevice(std::set<std::string>* devices) {
     // Add any additional devices required for dm-linear mappings.
     if (!IsDmLinearEnabled()) {
-        return true;
+        return;
     }
 
-    required_devices_partition_names_.emplace(super_partition_name_);
-    return true;
+    devices->emplace(super_partition_name_);
 }
 
-// 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() {
+// 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 (required_devices_partition_names_.empty()) {
+    if (devices.empty()) {
         return true;
     }
 
-    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+    auto uevent_callback = [&, this](const Uevent& uevent) {
+        return UeventCallback(uevent, &devices);
+    };
     uevent_listener_.RegenerateUevents(uevent_callback);
 
-    // UeventCallback() will remove found partitions from required_devices_partition_names_.
-    // So if it isn't empty here, it means some partitions are not found.
-    if (!required_devices_partition_names_.empty()) {
+    // UeventCallback() will remove found partitions from |devices|. So if it
+    // isn't empty here, it means some partitions are not found.
+    if (!devices.empty()) {
         LOG(INFO) << __PRETTY_FUNCTION__
                   << ": partition(s) not found in /sys, waiting for their uevent(s): "
-                  << android::base::Join(required_devices_partition_names_, ", ");
+                  << android::base::Join(devices, ", ");
         Timer t;
         uevent_listener_.Poll(uevent_callback, 10s);
         LOG(INFO) << "Wait for partitions returned after " << t;
     }
 
-    if (!required_devices_partition_names_.empty()) {
+    if (!devices.empty()) {
         LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
-                   << android::base::Join(required_devices_partition_names_, ", ");
+                   << android::base::Join(devices, ", ");
         return false;
     }
 
@@ -333,27 +341,20 @@
 }
 
 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;
         }
-        required_devices_partition_names_.emplace(partition_name);
+        devices.emplace(partition_name);
     }
-    if (required_devices_partition_names_.empty()) {
+    if (devices.empty()) {
         return true;
     }
-
-    auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
-    uevent_listener_.RegenerateUevents(uevent_callback);
-
-    if (!required_devices_partition_names_.empty()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
-                   << android::base::Join(required_devices_partition_names_, ", ");
-        return false;
-    }
-    return true;
+    return InitRequiredDevices(std::move(devices));
 }
 
 bool FirstStageMount::CreateLogicalPartitions() {
@@ -372,6 +373,11 @@
             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_);
         }
     }
@@ -387,20 +393,21 @@
     return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), lp_metadata_partition_);
 }
 
-ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
+ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent,
+                                                  std::set<std::string>* required_devices) {
     // Matches partition name to create device nodes.
     // Both required_devices_partition_names_ and uevent->partition_name have A/B
     // suffix when A/B is used.
-    auto iter = required_devices_partition_names_.find(name);
-    if (iter != required_devices_partition_names_.end()) {
+    auto iter = required_devices->find(name);
+    if (iter != required_devices->end()) {
         LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
         if (IsDmLinearEnabled() && name == super_partition_name_) {
             std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
             lp_metadata_partition_ = links[0];
         }
-        required_devices_partition_names_.erase(iter);
+        required_devices->erase(iter);
         device_handler_->HandleUevent(uevent);
-        if (required_devices_partition_names_.empty()) {
+        if (required_devices->empty()) {
             return ListenerAction::kStop;
         } else {
             return ListenerAction::kContinue;
@@ -409,18 +416,19 @@
     return ListenerAction::kContinue;
 }
 
-ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) {
+ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent,
+                                               std::set<std::string>* required_devices) {
     // 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);
+        return HandleBlockDevice(uevent.partition_name, uevent, required_devices);
     } else {
         size_t base_idx = uevent.path.rfind('/');
         if (base_idx != std::string::npos) {
-            return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent);
+            return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent, required_devices);
         }
     }
     // Not found a partition or find an unneeded partition, continue to find others.
@@ -577,17 +585,7 @@
     const auto devices = fs_mgr_overlayfs_required_devices(&fstab_);
     for (auto const& device : devices) {
         if (android::base::StartsWith(device, "/dev/block/by-name/")) {
-            required_devices_partition_names_.emplace(basename(device.c_str()));
-            auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
-            uevent_listener_.RegenerateUevents(uevent_callback);
-            if (!required_devices_partition_names_.empty()) {
-                uevent_listener_.Poll(uevent_callback, 10s);
-                if (!required_devices_partition_names_.empty()) {
-                    LOG(ERROR) << __PRETTY_FUNCTION__
-                               << ": partition(s) not found after polling timeout: "
-                               << android::base::Join(required_devices_partition_names_, ", ");
-                }
-            }
+            InitRequiredDevices({basename(device.c_str())});
         } else {
             InitMappedDevice(device);
         }
@@ -599,14 +597,14 @@
 }
 
 void FirstStageMount::UseGsiIfPresent() {
-    std::string metadata_file, error;
+    std::string error;
 
-    if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) {
+    if (!android::gsi::CanBootIntoGsi(&error)) {
         LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
         return;
     }
 
-    auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str());
+    auto metadata = android::fs_mgr::ReadFromImageFile(gsi::kDsuLpMetadataFile);
     if (!metadata) {
         LOG(ERROR) << "GSI partition layout could not be read";
         return;
@@ -630,18 +628,20 @@
         return;
     }
 
-    // Replace the existing system fstab entry.
-    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
-        return entry.mount_point == "/system";
-    });
-    if (system_partition != fstab_.end()) {
-        fstab_.erase(system_partition);
+    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 + ",";
     }
-    fstab_.emplace_back(BuildGsiSystemFstabEntry());
+    // Publish the logical partition names for TransformFstabForDsu
+    WriteFile(gsi::kGsiLpNamesFile, lp_names);
+    TransformFstabForDsu(&fstab_, dsu_partitions);
     gsi_not_on_userdata_ = (super_name != "userdata");
 }
 
-bool FirstStageMountVBootV1::GetDmVerityDevices() {
+bool FirstStageMountVBootV1::GetDmVerityDevices(std::set<std::string>* devices) {
     need_dm_verity_ = false;
 
     for (const auto& fstab_entry : fstab_) {
@@ -660,7 +660,7 @@
     // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
     for (const auto& fstab_entry : fstab_) {
         if (!fstab_entry.fs_mgr_flags.logical) {
-            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
+            devices->emplace(basename(fstab_entry.blk_device.c_str()));
         }
     }
 
@@ -711,7 +711,7 @@
     }
 }
 
-bool FirstStageMountVBootV2::GetDmVerityDevices() {
+bool FirstStageMountVBootV2::GetDmVerityDevices(std::set<std::string>* devices) {
     need_dm_verity_ = false;
 
     std::set<std::string> logical_partitions;
@@ -725,7 +725,7 @@
             // Don't try to find logical partitions via uevent regeneration.
             logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));
         } else {
-            required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str()));
+            devices->emplace(basename(fstab_entry.blk_device.c_str()));
         }
     }
 
@@ -742,11 +742,11 @@
             if (logical_partitions.count(partition_name)) {
                 continue;
             }
-            // required_devices_partition_names_ is of type std::set so it's not an issue
-            // to emplace a partition twice. e.g., /vendor might be in both places:
+            // 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:
             //   - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
             //   - mount_fstab_recs_: /vendor_a
-            required_devices_partition_names_.emplace(partition_name);
+            devices->emplace(partition_name);
         }
     }
     return true;
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
index 9c2ca75..bbebbe8 100644
--- a/init/fscrypt_init_extensions.cpp
+++ b/init/fscrypt_init_extensions.cpp
@@ -28,6 +28,7 @@
 
 #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>
@@ -38,7 +39,7 @@
 
 #define TAG "fscrypt"
 
-static int set_system_de_policy_on(const std::string& dir);
+static int set_policy_on(const std::string& ref_basename, const std::string& dir);
 
 int fscrypt_install_keyring() {
     key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, KEY_SPEC_SESSION_KEYRING);
@@ -104,7 +105,7 @@
     // 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.
-        set_system_de_policy_on(dir);
+        set_policy_on(fscrypt_key_ref, dir);
         return 0;
     }
 
@@ -135,7 +136,16 @@
             return 0;
         }
     }
-    int err = set_system_de_policy_on(dir);
+    std::vector<std::string> per_boot_directories = {
+            "per_boot",
+    };
+    for (const auto& d : per_boot_directories) {
+        if ((prefix + d) == dir) {
+            LOG(INFO) << "Setting per_boot key on " << dir;
+            return set_policy_on(fscrypt_key_per_boot_ref, dir);
+        }
+    }
+    int err = set_policy_on(fscrypt_key_ref, dir);
     if (err == 0) {
         return 0;
     }
@@ -147,40 +157,66 @@
         if ((prefix + d) == dir) {
             LOG(ERROR) << "Setting policy failed, deleting: " << dir;
             delete_dir_contents(dir);
-            err = set_system_de_policy_on(dir);
+            err = set_policy_on(fscrypt_key_ref, dir);
             break;
         }
     }
     return err;
 }
 
-static int set_system_de_policy_on(const std::string& dir) {
-    std::string ref_filename = std::string("/data") + fscrypt_key_ref;
-    std::string policy;
-    if (!android::base::ReadFileToString(ref_filename, &policy)) {
+static int parse_encryption_options_string(const std::string& options_string,
+                                           std::string* contents_mode_ret,
+                                           std::string* filenames_mode_ret,
+                                           int* policy_version_ret) {
+    auto parts = android::base::Split(options_string, ":");
+
+    if (parts.size() != 3) {
+        return -1;
+    }
+
+    *contents_mode_ret = parts[0];
+    *filenames_mode_ret = parts[1];
+    if (!android::base::StartsWith(parts[2], 'v') ||
+        !android::base::ParseInt(&parts[2][1], policy_version_ret)) {
+        return -1;
+    }
+
+    return 0;
+}
+
+// Set an encryption policy on the given directory.  The policy (key reference
+// and encryption options) to use is read from files that were written by vold.
+static int set_policy_on(const std::string& ref_basename, const std::string& dir) {
+    std::string ref_filename = std::string("/data") + ref_basename;
+    std::string key_ref;
+    if (!android::base::ReadFileToString(ref_filename, &key_ref)) {
         LOG(ERROR) << "Unable to read system policy to set on " << dir;
         return -1;
     }
 
-    auto type_filename = std::string("/data") + fscrypt_key_mode;
-    std::string modestring;
-    if (!android::base::ReadFileToString(type_filename, &modestring)) {
-        LOG(ERROR) << "Cannot read mode";
+    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 -1;
     }
 
-    std::vector<std::string> modes = android::base::Split(modestring, ":");
+    std::string contents_mode;
+    std::string filenames_mode;
+    int policy_version = 0;
 
-    if (modes.size() < 1 || modes.size() > 2) {
-        LOG(ERROR) << "Invalid encryption mode string: " << modestring;
+    if (parse_encryption_options_string(options_string, &contents_mode, &filenames_mode,
+                                        &policy_version)) {
+        LOG(ERROR) << "Invalid encryption options string: " << options_string;
         return -1;
     }
 
     int result =
-            fscrypt_policy_ensure(dir.c_str(), policy.c_str(), policy.length(), modes[0].c_str(),
-                                  modes.size() >= 2 ? modes[1].c_str() : "aes-256-cts");
+            fscrypt_policy_ensure(dir.c_str(), key_ref.c_str(), key_ref.length(),
+                                  contents_mode.c_str(), filenames_mode.c_str(), policy_version);
     if (result) {
         LOG(ERROR) << android::base::StringPrintf("Setting %02x%02x%02x%02x policy on %s failed!",
-                                                  policy[0], policy[1], policy[2], policy[3],
+                                                  key_ref[0], key_ref[1], key_ref[2], key_ref[3],
                                                   dir.c_str());
         return -1;
     }
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index f9a08a5..5dd5cf1 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -34,6 +34,11 @@
 namespace android {
 namespace init {
 
+// init.h
+inline void TriggerShutdown(const std::string&) {
+    abort();
+}
+
 // property_service.h
 inline bool CanReadProperty(const std::string&, const std::string&) {
     return true;
@@ -50,7 +55,7 @@
 
 // reboot_utils.h
 inline void SetFatalRebootTarget() {}
-inline void __attribute__((noreturn)) InitFatalReboot() {
+inline void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
     abort();
 }
 
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index b2402b3..522709e 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -30,6 +30,7 @@
 #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"
@@ -142,28 +143,46 @@
 #include "generated_stub_builtin_function_map.h"
 
 void PrintUsage() {
-    std::cout << "usage: host_init_verifier [-p FILE] -i FILE <init rc file>\n"
+    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"
-                 "-i FILE\tParse this JSON file for the HIDL interface inheritance hierarchy\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);
 
-    std::string interface_inheritance_hierarchy_file;
-
     while (true) {
         static const struct option long_options[] = {
                 {"help", no_argument, nullptr, 'h'},
                 {nullptr, 0, nullptr, 0},
         };
 
-        int arg = getopt_long(argc, argv, "p:i:", long_options, nullptr);
+        int arg = getopt_long(argc, argv, "p:", long_options, nullptr);
 
         if (arg == -1) {
             break;
@@ -176,9 +195,6 @@
             case 'p':
                 passwd_files.emplace_back(optarg);
                 break;
-            case 'i':
-                interface_inheritance_hierarchy_file = optarg;
-                break;
             default:
                 std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
                 return EXIT_FAILURE;
@@ -188,13 +204,12 @@
     argc -= optind;
     argv += optind;
 
-    if (argc != 1 || interface_inheritance_hierarchy_file.empty()) {
+    if (argc != 1) {
         PrintUsage();
         return EXIT_FAILURE;
     }
 
-    auto interface_inheritance_hierarchy_map =
-            ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file);
+    auto interface_inheritance_hierarchy_map = ReadInterfaceInheritanceHierarchy();
     if (!interface_inheritance_hierarchy_map) {
         LOG(ERROR) << interface_inheritance_hierarchy_map.error();
         return EXIT_FAILURE;
diff --git a/init/init.cpp b/init/init.cpp
index d4cbb5f..f775d8f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -19,7 +19,6 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <pthread.h>
-#include <seccomp_policy.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
@@ -28,6 +27,9 @@
 #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>
@@ -51,7 +53,6 @@
 #include <selinux/android.h>
 
 #include "action_parser.h"
-#include "boringssl_self_test.h"
 #include "builtins.h"
 #include "epoll.h"
 #include "first_stage_init.h"
@@ -61,6 +62,7 @@
 #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"
@@ -69,6 +71,7 @@
 #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;
@@ -90,16 +93,16 @@
 static char qemu[32];
 
 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::vector<Subcontext>* subcontexts;
+static std::unique_ptr<Subcontext> subcontext;
 
 void DumpState() {
     ServiceList::GetInstance().DumpState();
@@ -109,9 +112,10 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser(
-            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
+    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("import", std::make_unique<ImportParser>(&parser));
 
     return parser;
@@ -121,8 +125,8 @@
 Parser CreateServiceOnlyParser(ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser(
-            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
+                                               &service_list, subcontext.get(), std::nullopt));
     return parser;
 }
 
@@ -175,6 +179,16 @@
     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
@@ -183,16 +197,7 @@
     // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
     // commands to be executed.
     if (name == "sys.powerctl") {
-        // 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;
+        TriggerShutdown(value);
     }
 
     if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
@@ -575,15 +580,6 @@
     }
 }
 
-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";
@@ -615,6 +611,68 @@
                                 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();
@@ -631,9 +689,6 @@
         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.
@@ -686,7 +741,12 @@
     UmountDebugRamdisk();
     fs_mgr_vendor_overlay_mount_all();
     export_oem_lock_status();
-    StartPropertyService(&epoll);
+
+    StartPropertyService(&property_fd);
+    if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result) {
+        LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
+    }
+
     MountHandler mount_handler(&epoll);
     set_usb_controller();
 
@@ -697,7 +757,7 @@
         PLOG(FATAL) << "SetupMountNamespaces failed";
     }
 
-    subcontexts = InitializeSubcontexts();
+    subcontext = InitializeSubcontext();
 
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sm = ServiceList::GetInstance();
@@ -739,9 +799,6 @@
     // 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");
@@ -761,18 +818,16 @@
         // By default, sleep until something happens.
         auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
 
-        if (do_shutdown && !shutting_down) {
+        if (do_shutdown && !IsShuttingDown()) {
             do_shutdown = false;
-            if (HandlePowerctlMessage(shutdown_command)) {
-                shutting_down = true;
-            }
+            HandlePowerctlMessage(shutdown_command);
         }
 
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
         }
         if (!(waiting_for_prop || Service::is_exec_service_running())) {
-            if (!shutting_down) {
+            if (!IsShuttingDown()) {
                 auto next_process_action_time = HandleProcessActions();
 
                 // If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index cfc28f1..d884a94 100644
--- a/init/init.h
+++ b/init/init.h
@@ -31,9 +31,7 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
 Parser CreateServiceOnlyParser(ServiceList& service_list);
 
-bool HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
-
-void property_changed(const std::string& name, const std::string& value);
+void TriggerShutdown(const std::string& command);
 
 bool start_waiting_for_property(const char *name, const char *value);
 
@@ -41,6 +39,10 @@
 
 void ResetWaitForProp();
 
+void SendLoadPersistentPropertiesMessage();
+void SendStopSendingMessagesMessage();
+void SendStartSendingMessagesMessage();
+
 int SecondStageMain(int argc, char** argv);
 
 }  // namespace init
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0411214..9f63e4f 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -93,6 +93,26 @@
     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(
@@ -221,3 +241,19 @@
 
 }  // 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
index a54860f..1b76bba 100644
--- a/init/interface_utils.cpp
+++ b/init/interface_utils.cpp
@@ -21,7 +21,6 @@
 
 #include <android-base/strings.h>
 #include <hidl-util/FqInstance.h>
-#include <json/json.h>
 
 using android::FqInstance;
 using android::FQName;
@@ -42,41 +41,16 @@
 
 }  // namespace
 
-Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy(
-        const std::string& path) {
-    Json::Value root;
-    Json::Reader reader;
-    std::ifstream stream(path);
-    if (!reader.parse(stream, root)) {
-        return Error() << "Failed to read interface inheritance hierarchy file: " << path << "\n"
-                       << reader.getFormattedErrorMessages();
-    }
-
-    InterfaceInheritanceHierarchyMap result;
-    for (const Json::Value& entry : root) {
-        std::set<FQName> inherited_interfaces;
-        for (const Json::Value& intf : entry["inheritedInterfaces"]) {
-            FQName fqname;
-            if (!fqname.setTo(intf.asString())) {
-                return Error() << "Unable to parse interface '" << intf.asString() << "'";
-            }
-            inherited_interfaces.insert(fqname);
-        }
-        std::string intf_string = entry["interface"].asString();
-        FQName fqname;
-        if (!fqname.setTo(intf_string)) {
-            return Error() << "Unable to parse interface '" << intf_string << "'";
-        }
-        result[fqname] = inherited_interfaces;
-    }
-
-    return result;
-}
-
 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 << "'";
diff --git a/init/interface_utils.h b/init/interface_utils.h
index bd0c104..4ca377f 100644
--- a/init/interface_utils.h
+++ b/init/interface_utils.h
@@ -29,9 +29,6 @@
 
 using InterfaceInheritanceHierarchyMap = std::map<android::FQName, std::set<android::FQName>>;
 
-// Reads the HIDL interface inheritance hierarchy JSON file at the given path.
-Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy(const std::string& path);
-
 // 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.
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3408ff3..3baaf7c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -42,6 +42,7 @@
 #include <map>
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <queue>
 #include <thread>
 #include <vector>
@@ -63,8 +64,10 @@
 #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;
@@ -76,6 +79,7 @@
 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;
@@ -85,18 +89,14 @@
 namespace android {
 namespace init {
 
-static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
-
 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 {
@@ -164,6 +164,17 @@
     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();
 
@@ -199,7 +210,11 @@
     if (persistent_properties_loaded && StartsWith(name, "persist.")) {
         WritePersistentProperty(name, value);
     }
-    property_changed(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);
+    }
     return PROP_SUCCESS;
 }
 
@@ -239,35 +254,10 @@
     bool thread_started_ = false;
 };
 
-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 == kRestoreconProperty) {
-        LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty
-                   << "' from init; use the restorecon builtin directly";
-        return PROP_ERROR_INVALID_NAME;
-    }
-
-    uint32_t result = 0;
-    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
-    std::string error;
-    result = HandlePropertySet(name, value, kInitContext.c_str(), cr, &error);
-    if (result != PROP_SUCCESS) {
-        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
-    }
-
-    return result;
-}
-
 class SocketConnection {
   public:
     SocketConnection(int socket, const 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);
     }
@@ -304,6 +294,9 @@
     }
 
     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);
     }
@@ -318,7 +311,7 @@
         return true;
     }
 
-    int socket() { return socket_; }
+    [[nodiscard]] int Release() { return socket_.release(); }
 
     const ucred& cred() { return cred_; }
 
@@ -389,12 +382,46 @@
         return bytes_left == 0;
     }
 
-    int socket_;
+    unique_fd 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
@@ -462,15 +489,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, std::string* error) {
+                           const std::string& source_context, const ucred& cr,
+                           SocketConnection* socket, std::string* error) {
     if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
         return ret;
     }
 
     if (StartsWith(name, "ctl.")) {
-        return HandleControlMessage(name.c_str() + 4, value, cr.pid)
-                       ? PROP_SUCCESS
-                       : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+        return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
     }
 
     // sys.powerctl is a special property that is used to make the device reboot.  We want to log
@@ -501,6 +527,20 @@
     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 */
 
@@ -549,7 +589,8 @@
 
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
+        uint32_t result =
+                HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
         if (result != PROP_SUCCESS) {
             LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@@ -577,7 +618,7 @@
 
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
+        uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
         if (result != PROP_SUCCESS) {
             LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@@ -605,11 +646,16 @@
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
-    const char* context = kInitContext.c_str();
+    static constexpr const char* const kVendorPathPrefixes[2] = {
+            "/vendor",
+            "/odm",
+    };
+
+    const char* context = kInitContext;
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
-            if (StartsWith(filename, path_prefix)) {
-                context = secontext;
+        for (const auto& vendor_path_prefix : kVendorPathPrefixes) {
+            if (StartsWith(filename, vendor_path_prefix)) {
+                context = kVendorContext;
             }
         }
     }
@@ -741,33 +787,6 @@
     }
 }
 
-/* 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() {
@@ -945,6 +964,10 @@
         // 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.
@@ -962,6 +985,7 @@
         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);
@@ -985,21 +1009,97 @@
     selinux_android_restorecon(kPropertyInfosPath, 0);
 }
 
-void StartPropertyService(Epoll* epoll) {
+static void HandleInitSocket() {
+    auto message = ReadMessage(init_socket);
+    if (!message) {
+        LOG(ERROR) << "Could not read message from init_dedicated_recv_socket: " << message.error();
+        return;
+    }
+
+    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 {
-        PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
     }
 
     listen(property_set_fd, 8);
 
-    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
-        PLOG(FATAL) << result.error();
-    }
+    std::thread{PropertyServiceThread}.detach();
+
+    property_set = [](const std::string& key, const std::string& value) -> uint32_t {
+        android::base::SetProperty(key, value);
+        return 0;
+    };
 }
 
 }  // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 7f9f844..8f7d8d9 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -25,17 +25,15 @@
 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);
 
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr, std::string* error);
-
 void property_init();
 void property_load_boot_defaults(bool load_debug_prop);
-void load_persist_props();
-void StartPropertyService(Epoll* epoll);
+void StartPropertyService(int* epoll_socket);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service.proto b/init/property_service.proto
new file mode 100644
index 0000000..08268d9
--- /dev/null
+++ b/init/property_service.proto
@@ -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.
+ */
+
+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/proto_utils.h b/init/proto_utils.h
new file mode 100644
index 0000000..93a7d57
--- /dev/null
+++ b/init/proto_utils.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 b0b5b54..d77b975 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -22,6 +22,7 @@
 #include <linux/loop.h>
 #include <mntent.h>
 #include <semaphore.h>
+#include <stdlib.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -31,6 +32,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include <chrono>
 #include <memory>
 #include <set>
 #include <thread>
@@ -41,6 +43,7 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
@@ -50,16 +53,21 @@
 #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 "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::Timer;
@@ -69,6 +77,21 @@
 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. */
@@ -114,16 +137,16 @@
                     "-a",
                     mnt_fsname_.c_str(),
             };
-            android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG,
-                                    true, nullptr, nullptr, 0);
+            logwrap_fork_execvp(arraysize(f2fs_argv), f2fs_argv, &st, false, LOG_KLOG, true,
+                                nullptr);
         } else if (IsExt4()) {
             const char* ext4_argv[] = {
                     "/system/bin/e2fsck",
                     "-y",
                     mnt_fsname_.c_str(),
             };
-            android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG,
-                                    true, nullptr, nullptr, 0);
+            logwrap_fork_execvp(arraysize(ext4_argv), ext4_argv, &st, false, LOG_KLOG, true,
+                                nullptr);
         }
     }
 
@@ -161,8 +184,7 @@
 static void ShutdownVold() {
     const char* vdc_argv[] = {"/system/bin/vdc", "volume", "shutdown"};
     int status;
-    android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true, LOG_KLOG, true,
-                            nullptr, nullptr, 0);
+    logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true, nullptr);
 }
 
 static void LogShutdownTime(UmountStat stat, Timer* t) {
@@ -170,9 +192,23 @@
                  << stat;
 }
 
-/* Find all read+write block devices and emulated devices in /proc/mounts
- * and add them to correpsponding list.
- */
+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>* blockDevPartitions,
                                    std::vector<MountEntry>* emulatedPartitions, bool dump) {
     std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
@@ -205,8 +241,8 @@
     if (!security_getenforce()) {
         LOG(INFO) << "Run lsof";
         const char* lsof_argv[] = {"/system/bin/lsof"};
-        android_fork_execvp_ext(arraysize(lsof_argv), (char**)lsof_argv, &status, true, LOG_KLOG,
-                                true, nullptr, nullptr, 0);
+        logwrap_fork_execvp(arraysize(lsof_argv), lsof_argv, &status, false, LOG_KLOG, true,
+                            nullptr);
     }
     FindPartitionsToUmount(nullptr, nullptr, true);
     // dump current CPU stack traces and uninterruptible tasks
@@ -295,12 +331,15 @@
             LOG(ERROR) << "Reboot thread timed out";
 
             if (android::base::GetBoolProperty("ro.debuggable", false) == true) {
-                LOG(INFO) << "Try to dump init process call trace:";
-                const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
-                int status;
-                android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true,
-                                        LOG_KLOG, true, nullptr, nullptr, 0);
-
+                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);
 
@@ -424,6 +463,49 @@
     LOG(INFO) << "zram_backing_dev: `" << backing_dev << "` is cleared successfully.";
 }
 
+// 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"
@@ -436,6 +518,14 @@
     Timer t;
     LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
 
+    // 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, rebootTarget);
+        abort();
+    }
+
     // Ensure last reboot reason is reduced to canonical
     // alias reported in bootloader or system boot reason.
     size_t skip = 0;
@@ -480,12 +570,13 @@
     // 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 (kill_after_apps.count(s->name())) {
+        if (kDebuggingServices.count(s->name())) {
+            // keep debugging tools until non critical ones are all gone.
             s->SetShutdownCritical();
         } else if (to_starts.count(s->name())) {
             if (auto result = s->Start(); !result) {
@@ -499,6 +590,8 @@
                 LOG(ERROR) << "Could not start shutdown critical service '" << s->name()
                            << "': " << result.error();
             }
+        } else {
+            stop_first.push_back(s.get());
         }
     }
 
@@ -541,49 +634,12 @@
     // optional shutdown step
     // 1. terminate all services except shutdown critical ones. wait for delay to finish
     if (shutdown_timeout > 0ms) {
-        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;
+        StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);
     }
-
-    // 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();
-    }
+    // Send SIGKILL to ones that didn't terminate cleanly.
+    StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);
     SubcontextTerminate();
+    // Reap subcontext pids.
     ReapAnyOutstandingChildren();
 
     // 3. send volume shutdown to vold
@@ -595,9 +651,7 @@
         LOG(INFO) << "vold not running, skipping vold shutdown";
     }
     // logcat stopped here
-    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
-        if (kill_after_apps.count(s->name())) s->Stop();
-    }
+    StopServices(GetDebuggingServices(false /* only_post_data */), 0ms, false /* SIGKILL */);
     // 4. sync, try umount, and optionally run fsck for user shutdown
     {
         Timer sync_timer;
@@ -629,12 +683,96 @@
     abort();
 }
 
-bool HandlePowerctlMessage(const std::string& command) {
+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> 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/135984674): remount userdata
+    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";
+    }
+    // TODO(b/135984674): deactivate APEX modules and switch back 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) {
     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") {
         cmd = ANDROID_RB_POWEROFF;
@@ -654,6 +792,10 @@
         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" &&
@@ -680,7 +822,7 @@
                     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 false;
+                        return;
                     }
                 }
             } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
@@ -693,7 +835,7 @@
                 std::string err;
                 if (!write_bootloader_message(options, &err)) {
                     LOG(ERROR) << "Failed to set bootloader message: " << err;
-                    return false;
+                    return;
                 }
                 reboot_target = "recovery";
             }
@@ -708,7 +850,12 @@
     }
     if (command_invalid) {
         LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
-        return false;
+        return;
+    }
+
+    if (userspace_reboot) {
+        HandleUserspaceReboot();
+        return;
     }
 
     LOG(INFO) << "Clear action queue and start shutdown trigger";
@@ -722,15 +869,11 @@
     };
     ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
 
-    // Skip wait for prop if it is in progress
-    ResetWaitForProp();
+    EnterShutdown();
+}
 
-    // Clear EXEC flag if there is one pending
-    for (const auto& s : ServiceList::GetInstance()) {
-        s->UnSetExec();
-    }
-
-    return true;
+bool IsShuttingDown() {
+    return shutting_down;
 }
 
 }  // namespace init
diff --git a/init/reboot.h b/init/reboot.h
index 07dcb6e..81c3edc 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -23,8 +23,9 @@
 namespace init {
 
 // Parses and handles a setprop sys.powerctl message.
-bool HandlePowerctlMessage(const std::string& command);
+void HandlePowerctlMessage(const std::string& command);
 
+bool IsShuttingDown();
 }  // namespace init
 }  // namespace android
 
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index de085cc..dac0cf4 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -109,7 +109,7 @@
     abort();
 }
 
-void __attribute__((noreturn)) InitFatalReboot() {
+void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
     auto pid = fork();
 
     if (pid == -1) {
@@ -124,6 +124,7 @@
     }
 
     // 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)) {
@@ -154,7 +155,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();
+        InitFatalReboot(signal);
     };
     action.sa_flags = SA_RESTART;
     sigaction(SIGABRT, &action, nullptr);
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 3fd969e..878ad96 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();
+void __attribute__((noreturn)) InitFatalReboot(int signal_number);
 void InstallRebootSignalHandlers();
 
 }  // namespace init
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 4852cd0..a15d136 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -36,16 +36,18 @@
 // 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 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.
+//    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.
 
 #include "selinux.h"
 
@@ -228,6 +230,13 @@
                       "/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)) {
@@ -243,6 +252,13 @@
         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)) {
@@ -251,6 +267,7 @@
         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;
@@ -336,6 +353,17 @@
         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();
@@ -384,6 +412,12 @@
     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());
     }
diff --git a/init/service.cpp b/init/service.cpp
index 7a20966..c8568a0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -29,6 +29,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.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>
@@ -41,6 +42,7 @@
 #if defined(__ANDROID__)
 #include <ApexProperties.sysprop.h>
 
+#include "init.h"
 #include "mount_namespace.h"
 #include "property_service.h"
 #else
@@ -50,6 +52,7 @@
 using android::base::boot_clock;
 using android::base::GetProperty;
 using android::base::Join;
+using android::base::make_scope_guard;
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -250,6 +253,11 @@
         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;
@@ -292,7 +300,7 @@
                     LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
                                << (boot_completed ? "in 4 minutes" : "before boot completed");
                     // Notifies update_verifier and apexd
-                    property_set("ro.init.updatable_crashing", "1");
+                    property_set("sys.init.updatable_crashing", "1");
                 }
             }
         } else {
@@ -325,6 +333,12 @@
 
 
 Result<void> Service::ExecStart() {
+    auto reboot_on_failure = make_scope_guard([this] {
+        if (on_failure_reboot_target_) {
+            TriggerShutdown(*on_failure_reboot_target_);
+        }
+    });
+
     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.
@@ -345,10 +359,17 @@
               << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
               << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
 
+    reboot_on_failure.Disable();
     return {};
 }
 
 Result<void> Service::Start() {
+    auto reboot_on_failure = make_scope_guard([this] {
+        if (on_failure_reboot_target_) {
+            TriggerShutdown(*on_failure_reboot_target_);
+        }
+    });
+
     if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
         ServiceList::GetInstance().DelayService(*this);
         return Error() << "Cannot start an updatable service '" << name_
@@ -371,6 +392,7 @@
             flags_ |= SVC_RESTART;
         }
         // It is not an error to try to start a service that is already running.
+        reboot_on_failure.Disable();
         return {};
     }
 
@@ -419,6 +441,23 @@
 
     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);
@@ -438,16 +477,8 @@
             setenv(key.c_str(), value.c_str(), 1);
         }
 
-        for (const auto& socket : sockets_) {
-            if (auto result = socket.CreateAndPublish(scon); !result) {
-                LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
-            }
-        }
-
-        for (const auto& file : files_) {
-            if (auto result = file.CreateAndPublish(); !result) {
-                LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
-            }
+        for (const auto& descriptor : descriptors) {
+            descriptor.Publish();
         }
 
         if (auto result = WritePidToFiles(&writepid_files_); !result) {
@@ -459,7 +490,8 @@
         SetProcessAttributesAndCaps();
 
         if (!ExpandArgsAndExecv(args_, sigstop_)) {
-            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
+            PLOG(ERROR) << "cannot execv('" << args_[0]
+                        << "'). See the 'Debugging init' section of init's README.md for tips";
         }
 
         _exit(127);
@@ -532,6 +564,7 @@
     }
 
     NotifyStateChange("running");
+    reboot_on_failure.Disable();
     return {};
 }
 
diff --git a/init/service.h b/init/service.h
index ccefc8e..272c9f9 100644
--- a/init/service.h
+++ b/init/service.h
@@ -75,6 +75,7 @@
             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();
@@ -196,6 +197,8 @@
     bool post_data_ = false;
 
     bool running_at_post_data_reset_ = false;
+
+    std::optional<std::string> on_failure_reboot_target_;
 };
 
 }  // namespace init
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index dd552fb..e7808a9 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -83,6 +83,9 @@
 }
 
 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 {};
@@ -145,17 +148,21 @@
     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 << "'";
-    }
+    // 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.isFullyQualified()) {
+            return Error() << "Interface name not fully-qualified '" << interface_name << "'";
+        }
 
-    if (fq_name.isValidValueName()) {
-        return Error() << "Interface name must not be a value name '" << interface_name << "'";
+        if (fq_name.isValidValueName()) {
+            return Error() << "Interface name must not be a value name '" << interface_name << "'";
+        }
     }
 
     const std::string fullname = interface_name + "/" + instance_name;
@@ -306,6 +313,18 @@
     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)) {
@@ -413,6 +432,14 @@
     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") {
@@ -467,49 +494,42 @@
     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}},
+        {"capabilities",            {0,     kMax, &ServiceParser::ParseCapabilities}},
+        {"class",                   {1,     kMax, &ServiceParser::ParseClass}},
+        {"console",                 {0,     1,    &ServiceParser::ParseConsole}},
+        {"critical",                {0,     0,    &ServiceParser::ParseCritical}},
+        {"disabled",                {0,     0,    &ServiceParser::ParseDisabled}},
+        {"enter_namespace",         {2,     2,    &ServiceParser::ParseEnterNamespace}},
+        {"file",                    {2,     2,    &ServiceParser::ParseFile}},
+        {"group",                   {1,     NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
+        {"interface",               {2,     2,    &ServiceParser::ParseInterface}},
+        {"ioprio",                  {2,     2,    &ServiceParser::ParseIoprio}},
+        {"keycodes",                {1,     kMax, &ServiceParser::ParseKeycodes}},
+        {"memcg.limit_in_bytes",    {1,     1,    &ServiceParser::ParseMemcgLimitInBytes}},
+        {"memcg.limit_percent",     {1,     1,    &ServiceParser::ParseMemcgLimitPercent}},
+        {"memcg.limit_property",    {1,     1,    &ServiceParser::ParseMemcgLimitProperty}},
         {"memcg.soft_limit_in_bytes",
-                        {1,     1,    &ServiceParser::ParseMemcgSoftLimitInBytes}},
-        {"memcg.swappiness",
-                        {1,     1,    &ServiceParser::ParseMemcgSwappiness}},
-        {"namespace",   {1,     2,    &ServiceParser::ParseNamespace}},
-        {"oneshot",     {0,     0,    &ServiceParser::ParseOneshot}},
-        {"onrestart",   {1,     kMax, &ServiceParser::ParseOnrestart}},
-        {"oom_score_adjust",
-                        {1,     1,    &ServiceParser::ParseOomScoreAdjust}},
-        {"override",    {0,     0,    &ServiceParser::ParseOverride}},
-        {"priority",    {1,     1,    &ServiceParser::ParsePriority}},
-        {"restart_period",
-                        {1,     1,    &ServiceParser::ParseRestartPeriod}},
-        {"rlimit",      {3,     3,    &ServiceParser::ParseProcessRlimit}},
-        {"seclabel",    {1,     1,    &ServiceParser::ParseSeclabel}},
-        {"setenv",      {2,     2,    &ServiceParser::ParseSetenv}},
-        {"shutdown",    {1,     1,    &ServiceParser::ParseShutdown}},
-        {"sigstop",     {0,     0,    &ServiceParser::ParseSigstop}},
-        {"socket",      {3,     6,    &ServiceParser::ParseSocket}},
-        {"timeout_period",
-                        {1,     1,    &ServiceParser::ParseTimeoutPeriod}},
-        {"updatable",   {0,     0,    &ServiceParser::ParseUpdatable}},
-        {"user",        {1,     1,    &ServiceParser::ParseUser}},
-        {"writepid",    {1,     kMax, &ServiceParser::ParseWritepid}},
+                                    {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;
@@ -529,13 +549,8 @@
     filename_ = filename;
 
     Subcontext* restart_action_subcontext = nullptr;
-    if (subcontexts_) {
-        for (auto& subcontext : *subcontexts_) {
-            if (StartsWith(filename, subcontext.path_prefix())) {
-                restart_action_subcontext = &subcontext;
-                break;
-            }
-        }
+    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
+        restart_action_subcontext = subcontext_;
     }
 
     std::vector<std::string> str_args(args.begin() + 2, args.end());
diff --git a/init/service_parser.h b/init/service_parser.h
index 4729874..b1281f5 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -30,10 +30,10 @@
 class ServiceParser : public SectionParser {
   public:
     ServiceParser(
-            ServiceList* service_list, std::vector<Subcontext>* subcontexts,
+            ServiceList* service_list, Subcontext* subcontext,
             const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
         : service_list_(service_list),
-          subcontexts_(subcontexts),
+          subcontext_(subcontext),
           interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
           service_(nullptr) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
@@ -68,12 +68,14 @@
     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);
@@ -83,7 +85,7 @@
     bool IsValidName(const std::string& name) const;
 
     ServiceList* service_list_;
-    std::vector<Subcontext>* subcontexts_;
+    Subcontext* subcontext_;
     std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
     std::unique_ptr<Service> service_;
     std::string filename_;
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 836145d..93cffd8 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -16,17 +16,18 @@
 
 #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 <android-base/unique_fd.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
 #include <processgroup/processgroup.h>
@@ -122,11 +123,15 @@
     return {};
 }
 
-void ZapStdio() {
+void SetupStdio(bool stdio_to_kmsg) {
     auto fd = unique_fd{open("/dev/null", O_RDWR | O_CLOEXEC)};
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
+    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) {
@@ -138,37 +143,44 @@
     dup2(fd, 2);
 }
 
-void PublishDescriptor(const std::string& key, const std::string& name, int fd) {
-    std::string published_name = key + name;
+}  // 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);
 }
 
-}  // namespace
-
-Result<void> SocketDescriptor::CreateAndPublish(const std::string& global_context) const {
+Result<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {
     const auto& socket_context = context.empty() ? global_context : context;
-    auto result = CreateSocket(name, type, passcred, perm, uid, gid, socket_context);
+    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, perm, uid, gid, socket_context);
     if (!result) {
         return result.error();
     }
 
-    PublishDescriptor(ANDROID_SOCKET_ENV_PREFIX, name, *result);
-
-    return {};
+    return Descriptor(ANDROID_SOCKET_ENV_PREFIX + name, unique_fd(*result));
 }
 
-Result<void> FileDescriptor::CreateAndPublish() const {
+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.
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK)));
+    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 << "'";
@@ -179,9 +191,7 @@
 
     LOG(INFO) << "Opened file '" << name << "', flags " << flags;
 
-    PublishDescriptor(ANDROID_FILE_ENV_PREFIX, name, fd.release());
-
-    return {};
+    return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
 }
 
 Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
@@ -234,7 +244,7 @@
         if (setpgid(0, getpid()) == -1) {
             return ErrnoError() << "setpgid failed";
         }
-        ZapStdio();
+        SetupStdio(attr.stdio_to_kmsg);
     }
 
     for (const auto& rlimit : attr.rlimits) {
diff --git a/init/service_utils.h b/init/service_utils.h
index befce25..3f1071e 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -22,6 +22,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/unique_fd.h>
 #include <cutils/iosched_policy.h>
 
 #include "result.h"
@@ -29,6 +30,18 @@
 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;
@@ -38,14 +51,14 @@
     std::string context;
     bool passcred = false;
 
-    Result<void> CreateAndPublish(const std::string& global_context) const;
+    Result<Descriptor> Create(const std::string& global_context) const;
 };
 
 struct FileDescriptor {
     std::string name;
     std::string type;
 
-    Result<void> CreateAndPublish() const;
+    Result<Descriptor> Create() const;
 };
 
 struct NamespaceInfo {
@@ -64,6 +77,7 @@
     gid_t gid;
     std::vector<gid_t> supp_gids;
     int priority;
+    bool stdio_to_kmsg;
 };
 Result<void> SetProcessAttributes(const ProcessAttributes& attr);
 
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 984235d..9b2c7d9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -28,28 +28,31 @@
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 
+#include <thread>
+
 #include "init.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 bool ReapOneProcess() {
+static pid_t ReapOneProcess() {
     siginfo_t siginfo = {};
     // This returns a zombie pid or informs us that there are no zombies left to be reaped.
     // It does NOT reap the pid; that is done below.
     if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
         PLOG(ERROR) << "waitid failed";
-        return false;
+        return 0;
     }
 
     auto pid = siginfo.si_pid;
-    if (pid == 0) return false;
+    if (pid == 0) return 0;
 
     // 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.
@@ -92,7 +95,7 @@
         LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
     }
 
-    if (!service) return true;
+    if (!service) return pid;
 
     service->Reap(siginfo);
 
@@ -100,13 +103,33 @@
         ServiceList::GetInstance().RemoveService(*service);
     }
 
-    return true;
+    return pid;
 }
 
 void ReapAnyOutstandingChildren() {
-    while (ReapOneProcess()) {
+    while (ReapOneProcess() != 0) {
     }
 }
 
+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 30063f2..fac1020 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -17,11 +17,16 @@
 #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 00f91d8..79fc372 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -18,16 +18,17 @@
 
 #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__)
@@ -48,56 +49,8 @@
 
 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<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 {};
-}
-
-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)
@@ -131,14 +84,6 @@
         result = RunBuiltinFunction(map_result->function, 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 {
@@ -224,7 +169,10 @@
 
     SelabelInitialize();
 
-    property_set = SubcontextPropertySet;
+    property_set = [](const std::string& key, const std::string& value) -> uint32_t {
+        android::base::SetProperty(key, value);
+        return 0;
+    };
 
     auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
     subcontext_process.MainLoop();
@@ -280,6 +228,15 @@
     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();
@@ -311,15 +268,6 @@
         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());
@@ -365,13 +313,12 @@
 static std::vector<Subcontext> subcontexts;
 static bool shutting_down;
 
-std::vector<Subcontext>* InitializeSubcontexts() {
+std::unique_ptr<Subcontext> InitializeSubcontext() {
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
-            subcontexts.emplace_back(path_prefix, secontext);
-        }
+        return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
+                                            kVendorContext);
     }
-    return &subcontexts;
+    return nullptr;
 }
 
 bool SubcontextChildReap(pid_t pid) {
diff --git a/init/subcontext.h b/init/subcontext.h
index 591521f..bcaad29 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_SUBCONTEXT_H
-#define _INIT_SUBCONTEXT_H
+#pragma once
 
 #include <signal.h>
 
@@ -31,22 +30,21 @@
 namespace android {
 namespace init {
 
-extern const std::string kInitContext;
-extern const std::string kVendorContext;
-extern const char* const paths_and_secontexts[2][2];
+static constexpr const char kInitContext[] = "u:r:init:s0";
+static constexpr const char kVendorContext[] = "u:r:vendor_init:s0";
 
 class Subcontext {
   public:
-    Subcontext(std::string path_prefix, std::string context)
-        : path_prefix_(std::move(path_prefix)), context_(std::move(context)), pid_(0) {
+    Subcontext(std::vector<std::string> path_prefixes, std::string context)
+        : path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
         Fork();
     }
 
     Result<void> 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_; }
 
@@ -54,18 +52,16 @@
     void Fork();
     Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
 
-    std::string path_prefix_;
+    std::vector<std::string> path_prefixes_;
     std::string context_;
     pid_t pid_;
     android::base::unique_fd socket_;
 };
 
 int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::vector<Subcontext>* InitializeSubcontexts();
+std::unique_ptr<Subcontext> InitializeSubcontext();
 bool SubcontextChildReap(pid_t pid);
 void SubcontextTerminate();
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/subcontext.proto b/init/subcontext.proto
index c31f4fb..e68115e 100644
--- a/init/subcontext.proto
+++ b/init/subcontext.proto
@@ -38,10 +38,4 @@
         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 f6fee8a..ccef2f3 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -33,7 +33,7 @@
         return;
     }
 
-    auto subcontext = Subcontext("path", context);
+    auto subcontext = Subcontext({"path"}, context);
     free(context);
 
     while (state.KeepRunning()) {
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index dcbff82..9cac35e 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -52,7 +52,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);
@@ -224,12 +224,8 @@
 }  // namespace init
 }  // namespace android
 
-int main(int argc, char** argv) {
-    if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
-        auto test_function_map = android::init::BuildTestFunctionMap();
-        return android::init::SubcontextMain(argc, argv, &test_function_map);
-    }
-
-    testing::InitGoogleTest(&argc, argv);
-    return RUN_ALL_TESTS();
+// 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);
 }
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index ac633776..416d942 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -171,7 +171,7 @@
     return RegenerateUeventsForDir(d.get(), callback);
 }
 
-static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+static const char* kRegenerationPaths[] = {"/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 cffc1b9..59f91ee 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -113,10 +113,12 @@
 class ColdBoot {
   public:
     ColdBoot(UeventListener& uevent_listener,
-             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers)
+             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers,
+             bool enable_parallel_restorecon)
         : uevent_listener_(uevent_listener),
           uevent_handlers_(uevent_handlers),
-          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}
+          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4),
+          enable_parallel_restorecon_(enable_parallel_restorecon) {}
 
     void Run();
 
@@ -132,6 +134,8 @@
     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_;
@@ -155,7 +159,6 @@
 
         selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
     }
-    _exit(EXIT_SUCCESS);
 }
 
 void ColdBoot::GenerateRestoreCon(const std::string& directory) {
@@ -195,7 +198,10 @@
 
         if (pid == 0) {
             UeventHandlerMain(i, num_handler_subprocesses_);
-            RestoreConHandler(i, num_handler_subprocesses_);
+            if (enable_parallel_restorecon_) {
+                RestoreConHandler(i, num_handler_subprocesses_);
+            }
+            _exit(EXIT_SUCCESS);
         }
 
         subprocess_pids_.emplace(pid);
@@ -240,14 +246,20 @@
 
     RegenerateUevents();
 
-    selinux_android_restorecon("/sys", 0);
-    selinux_android_restorecon("/sys/devices", 0);
-    GenerateRestoreCon("/sys");
-    // takes long time for /sys/devices, parallelize it
-    GenerateRestoreCon("/sys/devices");
+    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);
+    }
+
     WaitForSubProcesses();
 
     android::base::SetProperty(kColdBootDoneProp, "true");
@@ -284,7 +296,8 @@
             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.firmware_directories),
+            std::move(ueventd_configuration.external_firmware_handlers)));
 
     if (ueventd_configuration.enable_modalias_handling) {
         std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
@@ -293,7 +306,8 @@
     UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
 
     if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
-        ColdBoot cold_boot(uevent_listener, uevent_handlers);
+        ColdBoot cold_boot(uevent_listener, uevent_handlers,
+                           ueventd_configuration.enable_parallel_restorecon);
         cold_boot.Run();
     }
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 8ee0cce..a74b247 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -88,18 +88,42 @@
     return {};
 }
 
-Result<void> ParseModaliasHandlingLine(std::vector<std::string>&& args,
-                                       bool* enable_modalias_handling) {
+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) {
     if (args.size() != 2) {
-        return Error() << "modalias_handling lines take exactly one parameter";
+        return Error() << args[0] << " lines take exactly one parameter";
     }
 
     if (args[1] == "enabled") {
-        *enable_modalias_handling = true;
+        *feature = true;
     } else if (args[1] == "disabled") {
-        *enable_modalias_handling = false;
+        *feature = false;
     } else {
-        return Error() << "modalias_handling takes either 'enabled' or 'disabled' as a parameter";
+        return Error() << args[0] << " takes either 'enabled' or 'disabled' as a parameter";
     }
 
     return {};
@@ -212,12 +236,18 @@
     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(ParseModaliasHandlingLine, _1,
+                               std::bind(ParseEnabledDisabledLine, _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 d476dec..eaafa5a 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_UEVENTD_PARSER_H
-#define _INIT_UEVENTD_PARSER_H
+#pragma once
 
 #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 9c1cedf..172ba0b 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -20,6 +20,8 @@
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 
+#include "firmware_handler.h"
+
 namespace android {
 namespace init {
 
@@ -93,7 +95,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) {
@@ -119,7 +121,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) {
@@ -135,7 +137,52 @@
             "/more",
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories});
+    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});
 }
 
 TEST(ueventd_parser, UeventSocketRcvbufSize) {
@@ -144,7 +191,25 @@
 uevent_socket_rcvbuf_size 8M
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, false, 8 * 1024 * 1024});
+    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});
 }
 
 TEST(ueventd_parser, AllTogether) {
@@ -178,7 +243,11 @@
 /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
 )";
@@ -208,10 +277,15 @@
             "/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,
-                                   false, uevent_socket_rcvbuf_size});
+    TestUeventdFile(ueventd_file,
+                    {subsystems, sysfs_permissions, permissions, firmware_directories,
+                     external_firmware_handlers, true, uevent_socket_rcvbuf_size, true});
 }
 
 // All of these lines are ill-formed, so test that there is 0 output.
@@ -230,6 +304,18 @@
 
 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/util.cpp b/init/util.cpp
index 0532375..40db838 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <pwd.h>
+#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -481,7 +482,7 @@
         return;
     }
 
-    InitFatalReboot();
+    InitFatalReboot(SIGABRT);
 }
 
 // The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index f8d5058..e000a00 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -79,7 +79,7 @@
       index_++;
       return *this;
     }
-    iterator& operator++(int increment) {
+    const iterator operator++(int increment) {
       index_ += increment;
       return *this;
     }
@@ -87,7 +87,7 @@
       index_--;
       return *this;
     }
-    iterator& operator--(int decrement) {
+    const iterator operator--(int decrement) {
       index_ -= decrement;
       return *this;
     }
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index e67b458..340572c 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -23,9 +23,6 @@
  */
 #define LOG_TAG "ashmem"
 
-#ifndef __ANDROID_VNDK__
-#include <dlfcn.h>
-#endif
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/ashmem.h>
@@ -42,11 +39,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
 
@@ -66,32 +63,6 @@
 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
@@ -215,25 +186,31 @@
     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;
 
-    int fd = -1;
-#ifndef __ANDROID_VNDK__
-    if (!openFd) {
-        openFd = initOpenAshmemFd();
+    if (ashmem_device_path.empty()) {
+        return -1;
     }
 
-    if (openFd) {
-        fd = openFd();
-    }
-#endif
-    if (fd < 0) {
-        fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
-    }
+    int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
     if (fd < 0) {
         return fd;
     }
@@ -485,11 +462,3 @@
 
     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 6c7655a..2ba1eb0 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -94,5 +94,3 @@
 
     return buf.st_size;
 }
-
-void ashmem_init() {}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index b29638c..5fb11a5 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -84,7 +84,7 @@
     { 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" },
-    { 00755, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
+    { 00751, 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 },
@@ -161,7 +161,6 @@
     { 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,9 +172,10 @@
     { 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.*" },
-    { 00440, AID_ROOT,      AID_ROOT,      0, "system/etc/recovery.img" },
+    { 00750, AID_ROOT,      AID_ROOT,      0, "vendor/bin/install-recovery.sh" },
     { 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 },
 
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index abc5068..d80caa6 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -26,7 +26,6 @@
 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/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index ae9dab5..e1e8230 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -128,6 +128,7 @@
 #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 */
diff --git a/libcutils/sched_policy_test.cpp b/libcutils/sched_policy_test.cpp
index a321c90..b9e2832 100644
--- a/libcutils/sched_policy_test.cpp
+++ b/libcutils/sched_policy_test.cpp
@@ -107,6 +107,18 @@
 
 TEST(SchedPolicy, get_sched_policy_name) {
     EXPECT_STREQ("bg", get_sched_policy_name(SP_BACKGROUND));
-    EXPECT_STREQ("error", get_sched_policy_name(SchedPolicy(-2)));
-    EXPECT_STREQ("error", get_sched_policy_name(SP_CNT));
+    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));
 }
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 4da8215..bff16c1 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -34,12 +34,9 @@
     if (atrace_marker_fd == -1) {
         ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
         atrace_enabled_tags = 0;
-        goto done;
+    } else {
+      atrace_enabled_tags = atrace_get_property();
     }
-
-    atrace_enabled_tags = atrace_get_property();
-
-done:
     atomic_store_explicit(&atrace_is_ready, true, memory_order_release);
 }
 
diff --git a/libion/include/ion/ion.h b/libion/include/ion/ion.h
index a60d24e..1480bd9 100644
--- a/libion/include/ion/ion.h
+++ b/libion/include/ion/ion.h
@@ -49,6 +49,7 @@
 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 1ecfc78..07b4caf 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -31,10 +31,12 @@
 #include <unistd.h>
 
 #include <ion/ion.h>
-#include "ion_4.12.h"
+#include <linux/ion_4.19.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);
@@ -75,6 +77,14 @@
     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;
diff --git a/libion/ion_4.12.h b/libion/kernel-headers/linux/ion_4.12.h
similarity index 81%
rename from libion/ion_4.12.h
rename to libion/kernel-headers/linux/ion_4.12.h
index 614510c..1af8284 100644
--- a/libion/ion_4.12.h
+++ b/libion/kernel-headers/linux/ion_4.12.h
@@ -22,27 +22,27 @@
 #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;
+    __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;
+    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;
+    __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)
diff --git a/libion/kernel-headers/linux/ion_4.19.h b/libion/kernel-headers/linux/ion_4.19.h
new file mode 100644
index 0000000..f5b59f1
--- /dev/null
+++ b/libion/kernel-headers/linux/ion_4.19.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   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
new file mode 100644
index 0000000..75fef39
--- /dev/null
+++ b/libion/original-kernel-headers/linux/ion_4.19.h
@@ -0,0 +1,170 @@
+/*
+ * 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 d3b4688..989e029 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -24,9 +24,11 @@
     srcs: [
         "allocate_test.cpp",
         "exit_test.cpp",
-    	"heap_query.cpp",
+        "heap_query.cpp",
+        "system_heap.cpp",
         "invalid_values_test.cpp",
         "ion_test_fixture.cpp",
         "map_test.cpp",
+        "modular_heap_check.cpp",
     ],
 }
diff --git a/libion/tests/heap_query.cpp b/libion/tests/heap_query.cpp
index bad3bbf..fed8030 100644
--- a/libion/tests/heap_query.cpp
+++ b/libion/tests/heap_query.cpp
@@ -15,6 +15,8 @@
  */
 
 #include <gtest/gtest.h>
+
+#include <ion/ion.h>
 #include "ion_test_fixture.h"
 
 class HeapQuery : public IonTest {};
@@ -23,5 +25,24 @@
     ASSERT_GT(ion_heaps.size(), 0);
 }
 
-// TODO: Check if we expect some of the default
-// heap types to be present on all devices.
+// 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/ion_4.12.h b/libion/tests/ion_4.12.h
deleted file mode 100644
index 614510c..0000000
--- a/libion/tests/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/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
index 4f254b8..c78fe41 100644
--- a/libion/tests/ion_test_fixture.h
+++ b/libion/tests/ion_test_fixture.h
@@ -18,8 +18,8 @@
 #define ION_TEST_FIXTURE_H_
 
 #include <gtest/gtest.h>
+#include <linux/ion_4.12.h>
 #include <vector>
-#include "ion_4.12.h"
 
 using ::testing::Test;
 
diff --git a/libnativeloader/utils.h b/libion/tests/modular_heap_check.cpp
similarity index 63%
copy from libnativeloader/utils.h
copy to libion/tests/modular_heap_check.cpp
index a1c2be5..5505c5a 100644
--- a/libnativeloader/utils.h
+++ b/libion/tests/modular_heap_check.cpp
@@ -13,14 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
 
-namespace android::nativeloader {
+#include <gtest/gtest.h>
 
-#if defined(__LP64__)
-#define LIB "lib64"
-#else
-#define LIB "lib"
-#endif
+#include <ion/ion.h>
+#include "ion_test_fixture.h"
 
-}  // namespace android::nativeloader
+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
new file mode 100644
index 0000000..fb63888
--- /dev/null
+++ b/libion/tests/system_heap.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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 dda491a..b388e95 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -16,17 +16,3 @@
     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
deleted file mode 100644
index fe89e62..0000000
--- a/libkeyutils/mini_keyctl.cpp
+++ /dev/null
@@ -1,90 +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 "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
new file mode 100644
index 0000000..a04a3db
--- /dev/null
+++ b/libkeyutils/mini_keyctl/Android.bp
@@ -0,0 +1,27 @@
+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
new file mode 100644
index 0000000..8aace9a
--- /dev/null
+++ b/libkeyutils/mini_keyctl/mini_keyctl.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
new file mode 100644
index 0000000..fb9503f
--- /dev/null
+++ b/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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/libnativeloader/utils.h b/libkeyutils/mini_keyctl/mini_keyctl_utils.h
similarity index 68%
copy from libnativeloader/utils.h
copy to libkeyutils/mini_keyctl/mini_keyctl_utils.h
index a1c2be5..cc31d29 100644
--- a/libnativeloader/utils.h
+++ b/libkeyutils/mini_keyctl/mini_keyctl_utils.h
@@ -13,14 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
 
-namespace android::nativeloader {
+#ifndef _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_
+#define _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_
 
-#if defined(__LP64__)
-#define LIB "lib64"
-#else
-#define LIB "lib"
-#endif
+#include <string>
 
-}  // namespace android::nativeloader
+#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
deleted file mode 100644
index 79b4680..0000000
--- a/libkeyutils/mini_keyctl_utils.cpp
+++ /dev/null
@@ -1,164 +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 <dirent.h>
-#include <errno.h>
-#include <error.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <keyutils.h>
-
-static constexpr int kMaxCertSize = 4096;
-
-static std::vector<std::string> SplitBySpace(const std::string& s) {
-  std::istringstream iss(s);
-  return std::vector<std::string>{std::istream_iterator<std::string>{iss},
-                                  std::istream_iterator<std::string>{}};
-}
-
-// Find the keyring id. Because request_key(2) syscall is not available or the key is
-// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
-// information in the descritption section depending on the key type, only the first word in the
-// keyring description is used for searching.
-static key_serial_t GetKeyringIdOrDie(const std::string& keyring_desc) {
-  // If the keyring id is already a hex number, directly convert it to keyring id
-  key_serial_t keyring_id;
-  if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) {
-    return keyring_id;
-  }
-
-  // Only keys allowed by SELinux rules will be shown here.
-  std::ifstream proc_keys_file("/proc/keys");
-  if (!proc_keys_file.is_open()) {
-    error(1, errno, "Failed to open /proc/keys");
-    return -1;
-  }
-
-  std::string line;
-  while (getline(proc_keys_file, line)) {
-    std::vector<std::string> tokens = SplitBySpace(line);
-    if (tokens.size() < 9) {
-      continue;
-    }
-    std::string key_id = "0x" + tokens[0];
-    std::string key_type = tokens[7];
-    // The key description may contain space.
-    std::string key_desc_prefix = tokens[8];
-    // The prefix has a ":" at the end
-    std::string key_desc_pattern = keyring_desc + ":";
-    if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
-      continue;
-    }
-    if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) {
-      error(1, 0, "Unexpected key format in /proc/keys: %s", key_id.c_str());
-      return -1;
-    }
-    return keyring_id;
-  }
-  return -1;
-}
-
-int Unlink(key_serial_t key, const std::string& keyring) {
-  key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
-  if (keyctl_unlink(key, keyring_id) < 0) {
-    error(1, errno, "Failed to unlink key %x from keyring %s", key, keyring.c_str());
-    return 1;
-  }
-  return 0;
-}
-
-int Add(const std::string& type, const std::string& desc, const std::string& data,
-        const std::string& keyring) {
-  if (data.size() > kMaxCertSize) {
-    error(1, 0, "Certificate too large");
-    return 1;
-  }
-
-  key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
-  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
-
-  if (key < 0) {
-    error(1, errno, "Failed to add key");
-    return 1;
-  }
-
-  std::cout << key << std::endl;
-  return 0;
-}
-
-int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
-  key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
-
-  // read from stdin to get the certificates
-  std::istreambuf_iterator<char> begin(std::cin), end;
-  std::string data(begin, end);
-
-  if (data.size() > kMaxCertSize) {
-    error(1, 0, "Certificate too large");
-    return 1;
-  }
-
-  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
-
-  if (key < 0) {
-    error(1, errno, "Failed to add key");
-    return 1;
-  }
-
-  std::cout << key << std::endl;
-  return 0;
-}
-
-int RestrictKeyring(const std::string& keyring) {
-  key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
-  if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
-    error(1, errno, "Cannot restrict keyring '%s'", keyring.c_str());
-    return 1;
-  }
-  return 0;
-}
-
-std::string RetrieveSecurityContext(key_serial_t key) {
-  // Simply assume this size is enough in practice.
-  const int kMaxSupportedSize = 256;
-  std::string context;
-  context.resize(kMaxSupportedSize);
-  long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
-  if (retval < 0) {
-    error(1, errno, "Cannot get security context of key %x", key);
-    return std::string();
-  }
-  if (retval > kMaxSupportedSize) {
-    error(1, 0, "The key has unexpectedly long security context than %d", kMaxSupportedSize);
-    return std::string();
-  }
-  context.resize(retval);
-  return context;
-}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
deleted file mode 100644
index 3616831..0000000
--- a/libkeyutils/mini_keyctl_utils.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.
- */
-
-#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 c38d8cd..c40c5ef 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,7 +15,6 @@
 //
 
 liblog_sources = [
-    "config_write.cpp",
     "log_event_list.cpp",
     "log_event_write.cpp",
     "logger_lock.cpp",
@@ -23,7 +22,6 @@
     "logger_read.cpp",
     "logger_write.cpp",
     "logprint.cpp",
-    "stderr_write.cpp",
 ]
 liblog_host_sources = [
     "fake_log_device.cpp",
@@ -108,7 +106,9 @@
     },
 
     cflags: [
+        "-Wall",
         "-Werror",
+        "-Wextra",
         // This is what we want to do:
         //  liblog_cflags := $(shell \
         //   sed -n \
diff --git a/liblog/README.md b/liblog/README.md
index 98bee9f..871399a 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -96,11 +96,6 @@
 
     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
 -----------
 
@@ -144,11 +139,6 @@
 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/config_write.cpp b/liblog/config_write.cpp
deleted file mode 100644
index d454379..0000000
--- a/liblog/config_write.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <log/log_transport.h>
-
-#include "config_write.h"
-#include "logger.h"
-
-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
deleted file mode 100644
index a901f13..0000000
--- a/liblog/config_write.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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 c0b0e69..f1ddff1 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,15 +32,19 @@
 static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
 
 struct android_log_transport_write fakeLoggerWrite = {
-    .node = {&fakeLoggerWrite.node, &fakeLoggerWrite.node},
-    .context.priv = &logFds,
     .name = "fake",
-    .available = NULL,
+    .logMask = 0,
+    .context.priv = &logFds,
+    .available = fakeAvailable,
     .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 935590d..7290789 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -96,20 +96,14 @@
  * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
  */
 int __android_log_print(int prio, const char* tag, const char* fmt, ...)
-#if defined(__GNUC__)
-    __attribute__((__format__(printf, 3, 4)))
-#endif
-    ;
+    __attribute__((__format__(printf, 3, 4)));
 
 /**
  * Equivalent to `__android_log_print`, but taking a `va_list`.
  * (If `__android_log_print` is like `printf`, this is like `vprintf`.)
  */
 int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap)
-#if defined(__GNUC__)
-    __attribute__((__format__(printf, 3, 0)))
-#endif
-    ;
+    __attribute__((__format__(printf, 3, 0)));
 
 /**
  * Writes an assertion failure to the log (as `ANDROID_LOG_FATAL`) and to
@@ -127,16 +121,9 @@
  * including the source filename and line number more conveniently than this
  * function.
  */
-void __android_log_assert(const char* cond, const char* tag, const char* fmt,
-                          ...)
-#if defined(__GNUC__)
-    __attribute__((__noreturn__))
-    __attribute__((__format__(printf, 3, 4)))
-#endif
-    ;
+void __android_log_assert(const char* cond, const char* tag, const char* fmt, ...)
+    __attribute__((__noreturn__)) __attribute__((__format__(printf, 3, 4)));
 
-#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().
@@ -163,7 +150,6 @@
 
   LOG_ID_MAX
 } log_id_t;
-#endif
 
 /**
  * Writes the constant string `text` to the log buffer `id`,
@@ -171,8 +157,7 @@
  *
  * 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`,
@@ -182,12 +167,8 @@
  *
  * Apps should use __android_log_print() instead.
  */
-int __android_log_buf_print(int bufID, int prio, const char* tag,
-                            const char* fmt, ...)
-#if defined(__GNUC__)
-    __attribute__((__format__(printf, 4, 5)))
-#endif
-    ;
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+    __attribute__((__format__(printf, 4, 5)));
 
 #ifdef __cplusplus
 }
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 2687b3a..f7ec208 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -16,6 +16,8 @@
 
 #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 5928649..90d1e76 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -22,7 +22,6 @@
 #endif
 #include <stdint.h> /* uint16_t, int32_t */
 #include <stdio.h>
-#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -65,21 +64,6 @@
 #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.
  */
@@ -164,10 +148,6 @@
  */
 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 636d417..deadf20 100644
--- a/liblog/include/log/log_event_list.h
+++ b/liblog/include/log/log_event_list.h
@@ -36,16 +36,11 @@
 /*
  * 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;
@@ -57,7 +52,6 @@
     float float32;
   } data;
 } android_log_list_element;
-#endif
 
 /*
  * Creates a context associated with an event tag to write elements to
@@ -104,8 +98,6 @@
 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 {
@@ -280,7 +272,6 @@
 };
 }
 #endif
-#endif
 
 #ifdef __cplusplus
 }
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index c052a50..c8fafe7 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -16,41 +16,19 @@
 
 #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, ...)
-#if defined(__GNUC__)
-    __attribute__((__format__(printf, 4, 5)))
-#endif
-    ;
+int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...)
+    __attribute__((__format__(printf, 4, 5)));
 
 /*
  * log_id_t helpers
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index fdef306..2079e7a 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -53,8 +53,6 @@
 /*
  * 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 */
@@ -64,13 +62,10 @@
   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_v2) */
@@ -81,13 +76,10 @@
   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) */
@@ -98,13 +90,10 @@
   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) */
@@ -116,7 +105,6 @@
   uint32_t uid;      /* generating process's uid */
   char msg[0]; /* the entry's payload */
 };
-#endif
 #pragma clang diagnostic pop
 
 /*
@@ -133,8 +121,6 @@
  */
 #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];
@@ -191,7 +177,6 @@
   }
 #endif
 };
-#endif
 
 struct logger;
 
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 09c9910..6b4458c 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -24,9 +24,6 @@
 #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))
@@ -35,11 +32,6 @@
 
 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 */
@@ -169,5 +161,3 @@
 } __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
deleted file mode 100644
index b48761a..0000000
--- a/liblog/include/log/log_transport.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-**
-** 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 8f4b187..7dfd914 100644
--- a/liblog/include/log/logprint.h
+++ b/liblog/include/log/logprint.h
@@ -16,7 +16,8 @@
 
 #pragma once
 
-#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.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 5e04148..d3b72bc 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__)) {
-  typeof_log_id_t id;
+  uint8_t id;
   uint16_t tid;
   log_time realtime;
 } android_log_header_t;
@@ -57,6 +57,18 @@
   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/log_event_list.cpp b/liblog/log_event_list.cpp
index b1b527c..7882c96 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -51,11 +51,9 @@
 typedef struct android_log_context_internal android_log_context_internal;
 
 static void init_context(android_log_context_internal* context, uint32_t tag) {
-  size_t needed;
-
   context->tag = tag;
   context->read_write_flag = kAndroidLoggerWrite;
-  needed = sizeof(uint8_t) + sizeof(uint8_t);
+  size_t needed = sizeof(android_event_list_t);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
   }
@@ -88,7 +86,6 @@
 
 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)));
@@ -144,7 +141,6 @@
 }
 
 int android_log_write_list_begin(android_log_context ctx) {
-  size_t needed;
   android_log_context_internal* context;
 
   context = (android_log_context_internal*)ctx;
@@ -155,7 +151,7 @@
     context->overflow = true;
     return -EOVERFLOW;
   }
-  needed = sizeof(uint8_t) + sizeof(uint8_t);
+  size_t needed = sizeof(android_event_list_t);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
     return -EIO;
@@ -169,84 +165,59 @@
   if (context->overflow) {
     return -EIO;
   }
-  context->storage[context->pos + 0] = EVENT_TYPE_LIST;
-  context->storage[context->pos + 1] = 0;
+  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->list[context->list_nest_depth] = context->pos + 1;
   context->count[context->list_nest_depth] = 0;
   context->pos += needed;
   return 0;
 }
 
-static inline void copy4LE(uint8_t* buf, uint32_t val) {
-  buf[0] = val & 0xFF;
-  buf[1] = (val >> 8) & 0xFF;
-  buf[2] = (val >> 16) & 0xFF;
-  buf[3] = (val >> 24) & 0xFF;
-}
-
 int android_log_write_int32(android_log_context ctx, int32_t value) {
-  size_t needed;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+  android_log_context_internal* context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
   if (context->overflow) {
     return -EIO;
   }
-  needed = sizeof(uint8_t) + sizeof(value);
+  size_t needed = sizeof(android_event_int_t);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
     return -EIO;
   }
   context->count[context->list_nest_depth]++;
-  context->storage[context->pos + 0] = EVENT_TYPE_INT;
-  copy4LE(&context->storage[context->pos + 1], value);
+  auto* event_int = reinterpret_cast<android_event_int_t*>(&context->storage[context->pos]);
+  event_int->type = EVENT_TYPE_INT;
+  event_int->data = value;
   context->pos += needed;
   return 0;
 }
 
-static inline void copy8LE(uint8_t* buf, uint64_t val) {
-  buf[0] = val & 0xFF;
-  buf[1] = (val >> 8) & 0xFF;
-  buf[2] = (val >> 16) & 0xFF;
-  buf[3] = (val >> 24) & 0xFF;
-  buf[4] = (val >> 32) & 0xFF;
-  buf[5] = (val >> 40) & 0xFF;
-  buf[6] = (val >> 48) & 0xFF;
-  buf[7] = (val >> 56) & 0xFF;
-}
-
 int android_log_write_int64(android_log_context ctx, int64_t value) {
-  size_t needed;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+  android_log_context_internal* context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
   if (context->overflow) {
     return -EIO;
   }
-  needed = sizeof(uint8_t) + sizeof(value);
+  size_t needed = sizeof(android_event_long_t);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
     return -EIO;
   }
   context->count[context->list_nest_depth]++;
-  context->storage[context->pos + 0] = EVENT_TYPE_LONG;
-  copy8LE(&context->storage[context->pos + 1], value);
+  auto* event_long = reinterpret_cast<android_event_long_t*>(&context->storage[context->pos]);
+  event_long->type = EVENT_TYPE_LONG;
+  event_long->data = value;
   context->pos += needed;
   return 0;
 }
 
 int android_log_write_string8_len(android_log_context ctx, const char* value, size_t maxlen) {
-  size_t needed;
-  ssize_t len;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+  android_log_context_internal* context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
@@ -256,8 +227,8 @@
   if (!value) {
     value = "";
   }
-  len = strnlen(value, maxlen);
-  needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+  int32_t len = strnlen(value, maxlen);
+  size_t needed = sizeof(android_event_string_t) + len;
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     /* Truncate string for delivery */
     len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
@@ -267,10 +238,11 @@
     }
   }
   context->count[context->list_nest_depth]++;
-  context->storage[context->pos + 0] = EVENT_TYPE_STRING;
-  copy4LE(&context->storage[context->pos + 1], len);
+  auto* event_string = reinterpret_cast<android_event_string_t*>(&context->storage[context->pos]);
+  event_string->type = EVENT_TYPE_STRING;
+  event_string->length = len;
   if (len) {
-    memcpy(&context->storage[context->pos + 5], value, len);
+    memcpy(&event_string->data, value, len);
   }
   context->pos += needed;
   return len;
@@ -281,26 +253,22 @@
 }
 
 int android_log_write_float32(android_log_context ctx, float value) {
-  size_t needed;
-  uint32_t ivalue;
-  android_log_context_internal* context;
-
-  context = (android_log_context_internal*)ctx;
+  android_log_context_internal* context = (android_log_context_internal*)ctx;
   if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
     return -EBADF;
   }
   if (context->overflow) {
     return -EIO;
   }
-  needed = sizeof(uint8_t) + sizeof(ivalue);
+  size_t needed = sizeof(android_event_float_t);
   if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
     context->overflow = true;
     return -EIO;
   }
-  ivalue = *(uint32_t*)&value;
   context->count[context->list_nest_depth]++;
-  context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
-  copy4LE(&context->storage[context->pos + 1], ivalue);
+  auto* event_float = reinterpret_cast<android_event_float_t*>(&context->storage[context->pos]);
+  event_float->type = EVENT_TYPE_FLOAT;
+  event_float->data = value;
   context->pos += needed;
   return 0;
 }
@@ -401,22 +369,6 @@
 }
 
 /*
- * 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
@@ -478,20 +430,22 @@
     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 + elem.len) > context->len) {
+      if ((pos + sizeof(android_event_int_t)) > context->len) {
         elem.type = EVENT_TYPE_UNKNOWN;
         return elem;
       }
-      elem.data.int32 = get4LE(&context->storage[pos]);
+
+      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 */
-      pos += elem.len;
       elem.complete = !context->list_nest_depth && !context->count[0];
       if (!peek) {
         if (!context->count[context->list_nest_depth] ||
@@ -501,16 +455,19 @@
         context->pos = pos;
       }
       return elem;
+    }
 
-    case EVENT_TYPE_LONG:
+    case EVENT_TYPE_LONG: {
       elem.len = sizeof(int64_t);
-      if ((pos + elem.len) > context->len) {
+      if ((pos + sizeof(android_event_long_t)) > context->len) {
         elem.type = EVENT_TYPE_UNKNOWN;
         return elem;
       }
-      elem.data.int64 = get8LE(&context->storage[pos]);
+
+      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 */
-      pos += elem.len;
       elem.complete = !context->list_nest_depth && !context->count[0];
       if (!peek) {
         if (!context->count[context->list_nest_depth] ||
@@ -520,15 +477,22 @@
         context->pos = pos;
       }
       return elem;
+    }
 
-    case EVENT_TYPE_STRING:
-      if ((pos + sizeof(int32_t)) > context->len) {
+    case EVENT_TYPE_STRING: {
+      if ((pos + sizeof(android_event_string_t)) > context->len) {
         elem.type = EVENT_TYPE_UNKNOWN;
         elem.complete = true;
         return elem;
       }
-      elem.len = get4LE(&context->storage[pos]);
-      pos += sizeof(int32_t);
+      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;
@@ -537,7 +501,7 @@
           return elem;
         }
       }
-      elem.data.string = (char*)&context->storage[pos];
+      elem.data.string = event_string->data;
       /* common tangeable object suffix */
       pos += elem.len;
       elem.complete = !context->list_nest_depth && !context->count[0];
@@ -549,13 +513,16 @@
         context->pos = pos;
       }
       return elem;
+    }
 
-    case EVENT_TYPE_LIST:
-      if ((pos + sizeof(uint8_t)) > context->len) {
+    case EVENT_TYPE_LIST: {
+      if ((pos + sizeof(android_event_list_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.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
       if (peek) {
         return elem;
@@ -563,15 +530,17 @@
       if (context->count[context->list_nest_depth]) {
         context->count[context->list_nest_depth]--;
       }
-      context->list_stop = !context->storage[pos];
+      context->list_stop = event_list->element_count == 0;
       context->list_nest_depth++;
       if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
-        context->count[context->list_nest_depth] = context->storage[pos];
+        context->count[context->list_nest_depth] = event_list->element_count;
       }
-      context->pos = pos + sizeof(uint8_t);
+      context->pos = pos;
       return elem;
+    }
 
     case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+      pos++;
       if (!peek) {
         context->pos = pos;
       }
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index eba305f..619cf8c 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <endian.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -24,6 +23,7 @@
 #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>
@@ -39,9 +39,6 @@
 #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);
@@ -67,16 +64,15 @@
                             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,
-    .getSize = logdGetSize,
     .setSize = logdSetSize,
+    .getSize = logdGetSize,
     .getReadableSize = logdGetReadableSize,
     .getPrune = logdGetPrune,
     .setPrune = logdSetPrune,
@@ -111,7 +107,7 @@
   strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
 
   int fd = socket(AF_LOCAL, type | SOCK_CLOEXEC, 0);
-  if (fd == 0) {
+  if (fd == -1) {
     return -1;
   }
 
@@ -280,13 +276,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;
   }
@@ -320,16 +316,11 @@
   return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
 }
 
-static void caught_signal(int signum __unused) {}
-
 static int logdOpen(struct android_log_logger_list* logger_list,
                     struct android_log_transport_context* transp) {
   struct android_log_logger* logger;
-  struct sigaction ignore;
-  struct sigaction old_sigaction;
-  unsigned int old_alarm = 0;
   char buffer[256], *cp, c;
-  int e, ret, remaining, sock;
+  int ret, remaining, sock;
 
   if (!logger_list) {
     return -EINVAL;
@@ -341,12 +332,6 @@
   }
 
   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;
@@ -363,7 +348,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 = ',';
@@ -371,7 +356,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;
   }
@@ -380,46 +365,30 @@
     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;
   }
 
-  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);
-  }
+  ret = TEMP_FAILURE_RETRY(write(sock, buffer, cp - buffer));
+  int write_errno = errno;
 
   if (ret <= 0) {
     close(sock);
-    if ((ret == -1) && e) {
-      return -e;
+    if (ret == -1) {
+      return -write_errno;
     }
     if (ret == 0) {
       return -EIO;
@@ -437,52 +406,21 @@
 /* Read from the selected logs */
 static int logdRead(struct android_log_logger_list* logger_list,
                     struct android_log_transport_context* transp, struct log_msg* log_msg) {
-  int ret, e;
-  struct sigaction ignore;
-  struct sigaction old_sigaction;
-  unsigned int old_alarm = 0;
-
-  ret = logdOpen(logger_list, transp);
+  int ret = logdOpen(logger_list, transp);
   if (ret < 0) {
     return ret;
   }
 
   memset(log_msg, 0, sizeof(*log_msg));
 
-  unsigned int new_alarm = 0;
-  if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-    if ((logger_list->mode & ANDROID_LOG_WRAP) &&
-        (logger_list->start.tv_sec || logger_list->start.tv_nsec)) {
-      /* b/64143705 */
-      new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10;
-      logger_list->mode &= ~ANDROID_LOG_WRAP;
-    } else {
-      new_alarm = 30;
-    }
-
-    memset(&ignore, 0, sizeof(ignore));
-    ignore.sa_handler = caught_signal;
-    sigemptyset(&ignore.sa_mask);
-    /* particularily useful if tombstone is reporting for logd */
-    sigaction(SIGALRM, &ignore, &old_sigaction);
-    old_alarm = alarm(new_alarm);
-  }
-
   /* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
-  ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0);
-  e = errno;
-
-  if (new_alarm) {
-    if ((ret == 0) || (e == EINTR)) {
-      e = EAGAIN;
-      ret = -1;
-    }
-    alarm(old_alarm);
-    sigaction(SIGALRM, &old_sigaction, NULL);
+  ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0));
+  if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) {
+    return -EAGAIN;
   }
 
-  if ((ret == -1) && e) {
-    return -e;
+  if (ret == -1) {
+    return -errno;
   }
   return ret;
 }
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index c3f72f4..06a2baf 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <endian.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -35,23 +34,19 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
-#include "config_write.h"
 #include "log_portability.h"
 #include "logger.h"
 #include "uio.h"
 
-/* branchless on many architectures. */
-#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-
 static int logdAvailable(log_id_t LogId);
 static int logdOpen();
 static void logdClose();
 static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
 
 struct android_log_transport_write logdLoggerWrite = {
-    .node = {&logdLoggerWrite.node, &logdLoggerWrite.node},
-    .context.sock = -EBADF,
     .name = "logd",
+    .logMask = 0,
+    .context.sock = -EBADF,
     .available = logdAvailable,
     .open = logdOpen,
     .close = logdClose,
@@ -184,9 +179,9 @@
       android_log_event_int_t buffer;
 
       header.id = LOG_ID_SECURITY;
-      buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+      buffer.header.tag = LIBLOG_LOG_TAG;
       buffer.payload.type = EVENT_TYPE_INT;
-      buffer.payload.data = htole32(snapshot);
+      buffer.payload.data = snapshot;
 
       newVec[headerLength].iov_base = &buffer;
       newVec[headerLength].iov_len = sizeof(buffer);
@@ -202,9 +197,9 @@
       android_log_event_int_t buffer;
 
       header.id = LOG_ID_EVENTS;
-      buffer.header.tag = htole32(LIBLOG_LOG_TAG);
+      buffer.header.tag = LIBLOG_LOG_TAG;
       buffer.payload.type = EVENT_TYPE_INT;
-      buffer.payload.data = htole32(snapshot);
+      buffer.payload.data = snapshot;
 
       newVec[headerLength].iov_base = &buffer;
       newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/liblog/logger.h b/liblog/logger.h
index 4b4ef5f..8cae66c 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -31,12 +31,9 @@
   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 */
@@ -54,7 +51,6 @@
 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 */
@@ -149,6 +145,4 @@
 int __android_log_trylock();
 void __android_log_unlock();
 
-extern int __android_log_transport;
-
 __END_DECLS
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 4fbab4b..e1772f1 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -25,27 +25,31 @@
 #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_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();
@@ -96,30 +100,13 @@
   }
 
   for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
-    if (node->write && (i != LOG_ID_KERNEL) &&
-        ((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) &&
-        (!node->available || ((*node->available)(static_cast<log_id_t>(i)) >= 0))) {
+    if (i != LOG_ID_KERNEL && (i != LOG_ID_SECURITY || check_log_uid_permissions() == 0) &&
+        (*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
@@ -128,7 +115,6 @@
  * 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
@@ -147,20 +133,14 @@
    * disengenuous use of this function.
    */
 
-  write_transport_for_each(transport, &__android_log_persist_write) {
-    if (transport->close) {
-      (*transport->close)();
-    }
+  if (android_log_write != nullptr) {
+    android_log_write->close();
   }
 
-  write_transport_for_each(transport, &__android_log_transport_write) {
-    if (transport->close) {
-      (*transport->close)();
-    }
+  if (android_log_persist_write != nullptr) {
+    android_log_persist_write->close();
   }
 
-  __android_log_config_write_close();
-
 #if defined(__ANDROID__)
   /*
    * Additional risk here somewhat mitigated by immediately unlock flushing
@@ -184,69 +164,39 @@
 #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() {
-  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) {
+  if (!transport_initialize(android_log_write)) {
     return -ENODEV;
   }
 
-  return ret;
-}
+  transport_initialize(android_log_persist_write);
 
-/*
- * 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);
+  return 1;
 }
 
 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__)
@@ -302,7 +252,7 @@
       }
     }
     if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
-      tag = android_lookupEventTag_len(m, &len, get4LE(static_cast<uint8_t*>(vec[0].iov_base)));
+      tag = android_lookupEventTag_len(m, &len, *static_cast<uint32_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 */
@@ -313,30 +263,9 @@
       return -EPERM;
     }
   } else {
-    /* Validate the incoming tag, tag content can not split across iovec */
-    char prio = ANDROID_LOG_VERBOSE;
-    const char* tag = static_cast<const char*>(vec[0].iov_base);
-    size_t len = vec[0].iov_len;
-    if (!tag) {
-      len = 0;
-    }
-    if (len > 0) {
-      prio = *tag;
-      if (len > 1) {
-        --len;
-        ++tag;
-      } else {
-        len = vec[1].iov_len;
-        tag = ((const char*)vec[1].iov_base);
-        if (!tag) {
-          len = 0;
-        }
-      }
-    }
-    /* tag must be nul terminated */
-    if (tag && strnlen(tag, len) >= len) {
-      tag = NULL;
-    }
+    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;
 
     if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
       errno = save_errno;
@@ -354,21 +283,18 @@
 #endif
 
   ret = 0;
-  i = 1 << log_id;
-  write_transport_for_each(node, &__android_log_transport_write) {
-    if (node->logMask & i) {
-      ssize_t retval;
-      retval = (*node->write)(log_id, &ts, vec, nr);
-      if (ret >= 0) {
-        ret = retval;
-      }
+  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;
     }
   }
 
-  write_transport_for_each(node, &__android_log_persist_write) {
-    if (node->logMask & i) {
-      (void)(*node->write)(log_id, &ts, vec, nr);
-    }
+  if (android_log_persist_write != nullptr && (android_log_persist_write->logMask & i)) {
+    android_log_persist_write->write(log_id, &ts, vec, nr);
   }
 
   errno = save_errno;
@@ -384,9 +310,6 @@
     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;
     }
@@ -576,81 +499,3 @@
 
   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();
-
-    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 3a54445..82fbafd 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -35,8 +35,10 @@
 #include <wchar.h>
 
 #include <cutils/list.h>
+
 #include <log/log.h>
 #include <log/logprint.h>
+#include <private/android_logger.h>
 
 #include "log_portability.h"
 
@@ -291,8 +293,10 @@
   return 1;
 }
 
+#ifndef __MINGW32__
 static const char tz[] = "TZ";
 static const char utc[] = "UTC";
+#endif
 
 /**
  * Returns FORMAT_OFF on invalid string
@@ -580,24 +584,6 @@
   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);
@@ -651,8 +637,7 @@
 
   if (eventDataLen < 1) return -1;
 
-  type = *eventData++;
-  eventDataLen--;
+  type = *eventData;
 
   cp = NULL;
   len = 0;
@@ -741,22 +726,24 @@
     case EVENT_TYPE_INT:
       /* 32-bit signed int */
       {
-        int32_t ival;
-
-        if (eventDataLen < 4) return -1;
-        ival = get4LE(eventData);
-        eventData += 4;
-        eventDataLen -= 4;
-
-        lval = ival;
+        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);
       }
       goto pr_lval;
     case EVENT_TYPE_LONG:
       /* 64-bit signed long */
-      if (eventDataLen < 8) return -1;
-      lval = get8LE(eventData);
-      eventData += 8;
-      eventDataLen -= 8;
+      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);
     pr_lval:
       outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
       if (outCount < outBufLen) {
@@ -770,14 +757,11 @@
     case EVENT_TYPE_FLOAT:
       /* float */
       {
-        uint32_t ival;
-        float fval;
-
-        if (eventDataLen < 4) return -1;
-        ival = get4LE(eventData);
-        fval = *(float*)&ival;
-        eventData += 4;
-        eventDataLen -= 4;
+        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);
 
         outCount = snprintf(outBuf, outBufLen, "%f", fval);
         if (outCount < outBufLen) {
@@ -792,12 +776,11 @@
     case EVENT_TYPE_STRING:
       /* UTF-8 chars, not NULL-terminated */
       {
-        unsigned int strLen;
-
-        if (eventDataLen < 4) return -1;
-        strLen = get4LE(eventData);
-        eventData += 4;
-        eventDataLen -= 4;
+        if (eventDataLen < 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);
 
         if (eventDataLen < strLen) {
           result = -1; /* mark truncated */
@@ -830,20 +813,19 @@
     case EVENT_TYPE_LIST:
       /* N items, all different types */
       {
-        unsigned char count;
-        int i;
+        if (eventDataLen < sizeof(android_event_list_t)) return -1;
+        auto* event_list = reinterpret_cast<const android_event_list_t*>(eventData);
 
-        if (eventDataLen < 1) return -1;
-
-        count = *eventData++;
-        eventDataLen--;
+        int8_t count = event_list->element_count;
+        eventData += sizeof(android_event_list_t);
+        eventDataLen -= sizeof(android_event_list_t);
 
         if (outBufLen <= 0) goto no_room;
 
         *outBuf++ = '[';
         outBufLen--;
 
-        for (i = 0; i < count; i++) {
+        for (int i = 0; i < count; i++) {
           result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen,
                                                 fmtStr, fmtLen);
           if (result != 0) goto bail;
@@ -1033,10 +1015,11 @@
     }
   }
   inCount = buf->len;
-  if (inCount < 4) return -1;
-  tagIndex = get4LE(eventData);
-  eventData += 4;
-  inCount -= 4;
+  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);
 
   entry->tagLen = 0;
   entry->tag = NULL;
@@ -1189,6 +1172,7 @@
   return p - begin;
 }
 
+#ifdef __ANDROID__
 static char* readSeconds(char* e, struct timespec* t) {
   unsigned long multiplier;
   char* p;
@@ -1229,7 +1213,6 @@
   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 81563bc..ce923f3 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -37,13 +37,12 @@
                      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,
@@ -63,58 +62,9 @@
   return -EBADF;
 }
 
-/* Determine the credentials of the caller */
-static bool uid_has_log_permission(uid_t uid) {
-  return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) || (uid == AID_LOGD);
-}
-
-static uid_t get_best_effective_uid() {
-  uid_t euid;
-  uid_t uid;
-  gid_t gid;
-  ssize_t i;
-  static uid_t last_uid = (uid_t)-1;
-
-  if (last_uid != (uid_t)-1) {
-    return last_uid;
-  }
-  uid = __android_log_uid();
-  if (uid_has_log_permission(uid)) {
-    return last_uid = uid;
-  }
-  euid = geteuid();
-  if (uid_has_log_permission(euid)) {
-    return last_uid = euid;
-  }
-  gid = getgid();
-  if (uid_has_log_permission(gid)) {
-    return last_uid = gid;
-  }
-  gid = getegid();
-  if (uid_has_log_permission(gid)) {
-    return last_uid = gid;
-  }
-  i = getgroups((size_t)0, NULL);
-  if (i > 0) {
-    gid_t list[i];
-
-    getgroups(i, list);
-    while (--i >= 0) {
-      if (uid_has_log_permission(list[i])) {
-        return last_uid = list[i];
-      }
-    }
-  }
-  return last_uid = uid;
-}
-
 static int pmsgClear(struct android_log_logger* logger __unused,
                      struct android_log_transport_context* transp __unused) {
-  if (uid_has_log_permission(get_best_effective_uid())) {
-    return unlink("/sys/fs/pstore/pmsg-ramoops-0");
-  }
-  errno = EPERM;
-  return -1;
+  return unlink("/sys/fs/pstore/pmsg-ramoops-0");
 }
 
 /*
@@ -129,15 +79,12 @@
                     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));
 
@@ -197,37 +144,30 @@
           ((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
            (logger_list->start.tv_nsec <= buf.l.realtime.tv_nsec)))) &&
         (!logger_list->pid || (logger_list->pid == buf.p.pid))) {
-      uid = get_best_effective_uid();
-      is_system = uid_has_log_permission(uid);
-      if (is_system || (uid == buf.p.uid)) {
-        char* msg = is_system ? log_msg->entry_v4.msg : log_msg->entry_v3.msg;
-        *msg = buf.prio;
-        fd = atomic_load(&transp->context.fd);
-        if (fd <= 0) {
-          return -EBADF;
-        }
-        ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
-        if (ret < 0) {
-          return -errno;
-        }
-        if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
-          return -EIO;
-        }
-
-        log_msg->entry_v4.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
-        log_msg->entry_v4.hdr_size =
-            is_system ? sizeof(log_msg->entry_v4) : sizeof(log_msg->entry_v3);
-        log_msg->entry_v4.pid = buf.p.pid;
-        log_msg->entry_v4.tid = buf.l.tid;
-        log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
-        log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
-        log_msg->entry_v4.lid = buf.l.id;
-        if (is_system) {
-          log_msg->entry_v4.uid = buf.p.uid;
-        }
-
-        return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
+      char* msg = log_msg->entry_v4.msg;
+      *msg = buf.prio;
+      fd = atomic_load(&transp->context.fd);
+      if (fd <= 0) {
+        return -EBADF;
       }
+      ret = TEMP_FAILURE_RETRY(read(fd, msg + sizeof(buf.prio), buf.p.len - sizeof(buf)));
+      if (ret < 0) {
+        return -errno;
+      }
+      if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
+        return -EIO;
+      }
+
+      log_msg->entry_v4.len = buf.p.len - sizeof(buf) + sizeof(buf.prio);
+      log_msg->entry_v4.hdr_size = sizeof(log_msg->entry_v4);
+      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;
+      log_msg->entry_v4.uid = buf.p.uid;
+
+      return ret + sizeof(buf.prio) + log_msg->entry_v4.hdr_size;
     }
 
     fd = atomic_load(&transp->context.fd);
@@ -275,13 +215,7 @@
   struct android_log_transport_context transp;
   struct content {
     struct listnode node;
-    union {
-      struct logger_entry_v4 entry;
-      struct logger_entry_v4 entry_v4;
-      struct logger_entry_v3 entry_v3;
-      struct logger_entry_v2 entry_v2;
-      struct logger_entry entry_v1;
-    };
+    struct logger_entry_v4 entry;
   } * content;
   struct names {
     struct listnode node;
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 4632b32..54980d9 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -29,7 +29,6 @@
 #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"
@@ -40,9 +39,9 @@
 static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
 
 struct android_log_transport_write pmsgLoggerWrite = {
-    .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
-    .context.fd = -1,
     .name = "pmsg",
+    .logMask = 0,
+    .context.fd = -1,
     .available = pmsgAvailable,
     .open = pmsgOpen,
     .close = pmsgClose,
@@ -87,13 +86,6 @@
   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];
@@ -107,7 +99,7 @@
       return -EINVAL;
     }
 
-    if (SNET_EVENT_LOG_TAG != get4LE(static_cast<uint8_t*>(vec[0].iov_base))) {
+    if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
       return -EPERM;
     }
   }
diff --git a/liblog/stderr_write.cpp b/liblog/stderr_write.cpp
deleted file mode 100644
index e76673f..0000000
--- a/liblog/stderr_write.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * stderr write handler.  Output is logcat-like, and responds to
- * logcat's environment variables ANDROID_PRINTF_LOG and
- * ANDROID_LOG_TAGS to filter output.
- *
- * This transport only provides a writer, that means that it does not
- * provide an End-To-End capability as the logs are effectively _lost_
- * to the stderr file stream.  The purpose of this transport is to
- * supply a means for command line tools to report their logging
- * to the stderr stream, in line with all other activities.
- */
-
-#include <errno.h>
-#include <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 d9d1a21..394fa93 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -54,8 +54,7 @@
     ],
     srcs: [
         "libc_test.cpp",
-        "liblog_test_default.cpp",
-        "liblog_test_stderr.cpp",
+        "liblog_test.cpp",
         "log_id_test.cpp",
         "log_radio_test.cpp",
         "log_read_test.cpp",
@@ -69,6 +68,7 @@
         "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 21d12a1..56892a2 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 <sys/endian.h>
+#include <sched.h>
 #include <sys/socket.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -30,7 +30,6 @@
 #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();
@@ -74,21 +73,6 @@
 }
 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())
@@ -227,14 +211,14 @@
   buffer.header.tag = 0;
   buffer.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer.payload.data = htole32(snapshot);
+  buffer.payload.data = snapshot;
 
   newVec[2].iov_base = &buffer;
   newVec[2].iov_len = sizeof(buffer);
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer.payload.data = htole32(snapshot);
+    buffer.payload.data = snapshot;
     writev(pstore_fd, newVec, nr);
   }
   state.PauseTiming();
@@ -303,11 +287,11 @@
   buffer->payload.header.tag = 0;
   buffer->payload.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer->payload.payload.data = htole32(snapshot);
+  buffer->payload.payload.data = snapshot;
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer->payload.payload.data = htole32(snapshot);
+    buffer->payload.payload.data = snapshot;
     write(pstore_fd, &buffer->pmsg_header,
           sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
               sizeof(android_log_event_int_t));
@@ -378,11 +362,11 @@
   buffer->payload.header.tag = 0;
   buffer->payload.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer->payload.payload.data = htole32(snapshot);
+  buffer->payload.payload.data = snapshot;
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer->payload.payload.data = htole32(snapshot);
+    buffer->payload.payload.data = snapshot;
     write(pstore_fd, &buffer->pmsg_header,
           sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
               sizeof(android_log_event_int_t));
@@ -453,11 +437,11 @@
   buffer->payload.header.tag = 0;
   buffer->payload.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer->payload.payload.data = htole32(snapshot);
+  buffer->payload.payload.data = snapshot;
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer->payload.payload.data = htole32(snapshot);
+    buffer->payload.payload.data = snapshot;
     write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
   }
   state.PauseTiming();
@@ -526,11 +510,11 @@
   buffer->payload.header.tag = 0;
   buffer->payload.payload.type = EVENT_TYPE_INT;
   uint32_t snapshot = 0;
-  buffer->payload.payload.data = htole32(snapshot);
+  buffer->payload.payload.data = snapshot;
 
   while (state.KeepRunning()) {
     ++snapshot;
-    buffer->payload.payload.data = htole32(snapshot);
+    buffer->payload.payload.data = snapshot;
     write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
   }
   state.PauseTiming();
@@ -619,13 +603,6 @@
 }
 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).
@@ -640,15 +617,6 @@
 }
 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 5a73dc0..94c4fbb 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -27,10 +27,12 @@
 #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>
@@ -38,25 +40,13 @@
 #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>
 
-#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
+using android::base::make_scope_guard;
 
-#ifdef USING_LOGGER_STDERR
-#define SUPPORTS_END_TO_END 0
-#else
-#define SUPPORTS_END_TO_END 1
-#endif
+// #define ENABLE_FLAKY_TESTS
 
 // 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
@@ -72,24 +62,93 @@
     _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__) && defined(USING_LOGGER_DEFAULT))
+#if defined(__ANDROID__)
 static std::string popenToString(const std::string& command) {
   std::string ret;
 
@@ -160,80 +219,60 @@
 
 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);
-  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
+  log_time ts1(ts);
 
-  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) {
+  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;
 #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);
-  }
-#endif
-  usleep(1000000);
+      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);
+    }
+  };
 
   int count = 0;
   int second_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);
-
+  auto check_function = [&](log_msg log_msg, bool* found) {
     if ((log_msg.entry.len != sizeof(android_log_event_long_t)) ||
         (log_msg.id() != LOG_ID_EVENTS)) {
-      continue;
+      return;
     }
 
     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)) {
-      continue;
+      return;
     }
 
     log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
@@ -242,171 +281,61 @@
     } else if (ts1 == tx) {
       ++second_count;
     }
-  }
 
-  EXPECT_EQ(SUPPORTS_END_TO_END, count);
-  EXPECT_EQ(SUPPORTS_END_TO_END, second_count);
+    if (count == 1 && second_count == 1) {
+      count = 0;
+      second_count = 0;
+      *found = true;
+    }
+  };
 
-  android_logger_list_close(logger_list);
+  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+
 #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 logger_list* logger_list;
-  ASSERT_TRUE(NULL !=
-              (logger_list = android_logger_list_open(
-                   LOG_ID_EVENTS, 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;
 
-  log_time ts(CLOCK_MONOTONIC);
-  EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+  std::string expected_message =
+      std::string(&prio, sizeof(prio)) + tag + std::string("", 1) + buf + std::string("", 1);
 
-  usleep(1000000);
+  auto write_function = [&] { ASSERT_LT(0, __android_log_write(prio, tag, buf.c_str())); };
 
-  int count = 0;
-
-  for (;;) {
-    log_msg log_msg;
-    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-      break;
+  auto check_function = [&](log_msg log_msg, bool* found) {
+    if (log_msg.entry.len != expected_message.length()) {
+      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;
+    if (expected_message != std::string(log_msg.msg(), log_msg.entry.len)) {
+      return;
     }
 
-    android_log_event_long_t* eventData;
-    eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+    *found = true;
+  };
 
-    if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
-      continue;
-    }
+  RunLogTests(LOG_ID_MAIN, write_function, check_function);
 
-    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 TEST_PREFIX
-  TEST_PREFIX
-  struct logger_list* logger_list;
-
+#ifdef __ANDROID__
   pid_t pid = getpid();
 
-  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) {
@@ -428,36 +357,25 @@
     ++cp;
     ++total;
   }
-  usleep(1000000);
 
-  int count = 0;
+  auto write_function = [&] { EXPECT_LT(0, __android_log_bswrite(0, message)); };
 
-  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;
+  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;
     }
 
     android_log_event_string_t* eventData;
     eventData = reinterpret_cast<android_log_event_string_t*>(log_msg.msg());
 
     if (!eventData || (eventData->type != EVENT_TYPE_STRING)) {
-      continue;
+      return;
     }
 
-    size_t len = get4LE(reinterpret_cast<char*>(&eventData->length));
+    size_t len = eventData->length;
     if (len == total) {
-      ++count;
+      *found = true;
 
       AndroidLogFormat* logformat = android_log_format_new();
       EXPECT_TRUE(NULL != logformat);
@@ -484,11 +402,10 @@
       }
       android_log_format_free(logformat);
     }
-  }
+  };
 
-  EXPECT_EQ(SUPPORTS_END_TO_END, count);
+  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
 
-  android_logger_list_close(logger_list);
 #else
   message = NULL;
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -516,26 +433,15 @@
 }
 
 static void buf_write_test(const char* message) {
-#ifdef TEST_PREFIX
-  TEST_PREFIX
-  struct logger_list* logger_list;
-
+#ifdef __ANDROID__
   pid_t pid = getpid();
 
-  ASSERT_TRUE(
-      NULL !=
-      (logger_list = android_logger_list_open(
-           LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
   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));
+  auto write_function = [&] {
+    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) {
@@ -552,26 +458,13 @@
     }
     ++cp;
   }
-  usleep(1000000);
 
-  int count = 0;
-
-  for (;;) {
-    log_msg log_msg;
-    if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-      break;
+  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;
     }
 
-    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;
+    *found = true;
 
     AndroidLogFormat* logformat = android_log_format_new();
     EXPECT_TRUE(NULL != logformat);
@@ -588,11 +481,10 @@
                 android_log_printLogLine(logformat, fileno(stderr), &entry));
     }
     android_log_format_free(logformat);
-  }
+  };
 
-  EXPECT_EQ(SUPPORTS_END_TO_END, count);
+  RunLogTests(LOG_ID_MAIN, write_function, check_function);
 
-  android_logger_list_close(logger_list);
 #else
   message = NULL;
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -611,8 +503,8 @@
   buf_write_test("\n Hello World \n");
 }
 
-#ifdef USING_LOGGER_DEFAULT  // requires blocking reader functionality
-#ifdef TEST_PREFIX
+#ifdef ENABLE_FLAKY_TESTS
+#ifdef __ANDROID__
 static unsigned signaled;
 static log_time signal_time;
 
@@ -673,8 +565,7 @@
 #endif
 
 TEST(liblog, android_logger_list_read__cpu_signal) {
-#ifdef TEST_PREFIX
-  TEST_PREFIX
+#ifdef __ANDROID__
   struct logger_list* logger_list;
   unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
@@ -766,7 +657,7 @@
 #endif
 }
 
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
 /*
  *  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
@@ -830,8 +721,7 @@
 #endif
 
 TEST(liblog, android_logger_list_read__cpu_thread) {
-#ifdef TEST_PREFIX
-  TEST_PREFIX
+#ifdef __ANDROID__
   struct logger_list* logger_list;
   unsigned long long v = 0xDEADBEAFA55A0000ULL;
 
@@ -923,13 +813,8 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // USING_LOGGER_DEFAULT
+#endif  // ENABLE_FLAKY_TESTS
 
-#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\
@@ -1062,41 +947,27 @@
 when you depart from me, sorrow abides and happiness\n\
 takes his leave.";
 
-#ifdef USING_LOGGER_DEFAULT
 TEST(liblog, max_payload) {
-#ifdef TEST_PREFIX
-  TEST_PREFIX
+#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)
+
   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);
 
-  LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
-                                            tag, max_payload_buf));
-  sleep(2);
+  auto write_function = [&] {
+    LOG_FAILURE_RETRY(
+        __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, max_payload_buf));
+  };
 
-  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;
-
-  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;
-    }
-
+  auto check_function = [&](log_msg log_msg, bool* found) {
     char* data = log_msg.msg();
 
     if (!data || strcmp(++data, tag)) {
-      continue;
+      return;
     }
 
     data += strlen(data) + 1;
@@ -1113,66 +984,31 @@
     }
 
     if (max_len > 512) {
-      matches = true;
-      break;
+      *found = true;
     }
-  }
+  };
 
-  android_logger_list_close(logger_list);
-
-#if SUPPORTS_END_TO_END
-  EXPECT_EQ(true, matches);
+  RunLogTests(LOG_ID_SYSTEM, write_function, check_function);
 
   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
 }
-#endif
 
 TEST(liblog, __android_log_buf_print__maxtag) {
-#ifdef TEST_PREFIX
-  TEST_PREFIX
-  struct logger_list* logger_list;
-
-  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
+  auto write_function = [&] {
+    EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO, max_payload_buf,
+                                         max_payload_buf));
+  };
 
-  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;
+  auto check_function = [&](log_msg log_msg, bool* found) {
+    if ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD) {
+      return;
     }
 
-    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;
+    *found = true;
 
     AndroidLogFormat* logformat = android_log_format_new();
     EXPECT_TRUE(NULL != logformat);
@@ -1190,19 +1026,20 @@
       EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD * 13 / 8, printLogLine);
     }
     android_log_format_free(logformat);
-  }
+  };
 
-  EXPECT_EQ(SUPPORTS_END_TO_END, count);
+  RunLogTests(LOG_ID_MAIN, write_function, check_function);
 
-  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 TEST_PREFIX
-  TEST_PREFIX
+#ifdef __ANDROID__
   pid_t pid = getpid();
   static const char big_payload_tag[] = "TEST_big_payload_XXXX";
   char tag[sizeof(big_payload_tag)];
@@ -1210,32 +1047,18 @@
   snprintf(tag + sizeof(tag) - 5, 5, "%04X", pid & 0xFFFF);
 
   std::string longString(3266519, 'x');
+  ssize_t ret;
 
-  ssize_t ret = LOG_FAILURE_RETRY(__android_log_buf_write(
-      LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, longString.c_str()));
+  auto write_function = [&] {
+    ret = LOG_FAILURE_RETRY(
+        __android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, tag, longString.c_str()));
+  };
 
-  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;
-    }
-
+  auto check_function = [&](log_msg log_msg, bool* found) {
     char* data = log_msg.msg();
 
     if (!data || strcmp(++data, tag)) {
-      continue;
+      return;
     }
 
     data += strlen(data) + 1;
@@ -1247,38 +1070,38 @@
       ++right;
     }
 
-    if (max_len <= (left - data)) {
-      max_len = left - data + 1;
+    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;
     }
-  }
+  };
 
-  android_logger_list_close(logger_list);
+  RunLogTests(LOG_ID_SYSTEM, write_function, check_function);
 
-#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
 }
 
-#ifdef USING_LOGGER_DEFAULT
 TEST(liblog, dual_reader) {
-#ifdef TEST_PREFIX
-  TEST_PREFIX
+#ifdef __ANDROID__
+  static const int expected_count1 = 25;
+  static const int expected_count2 = 25;
 
-  static const int num = 25;
+  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);
 
   for (int i = 25; i > 0; --i) {
     static const char fmt[] = "dual_reader %02d";
@@ -1287,32 +1110,46 @@
     LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                               "liblog", buffer));
   }
-  usleep(1000000);
 
-  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)));
+  alarm(2);
+  auto alarm_guard = android::base::make_scope_guard([] { alarm(0); });
 
-  struct logger_list* logger_list2;
+  // Wait until we see all messages with the blocking reader.
+  int count1 = 0;
+  int count2 = 0;
 
-  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);
+  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++;
+    }
   }
 
-  int count1 = 0;
+  // 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;
   bool done1 = false;
-  int count2 = 0;
   bool done2 = false;
 
-  do {
+  while (!done1 || !done2) {
     log_msg log_msg;
 
     if (!done1) {
-      if (android_logger_list_read(logger_list1, &log_msg) <= 0) {
+      if (android_logger_list_read(logger_list_non_block1.get(), &log_msg) <= 0) {
         done1 = true;
       } else {
         ++count1;
@@ -1320,26 +1157,21 @@
     }
 
     if (!done2) {
-      if (android_logger_list_read(logger_list2, &log_msg) <= 0) {
+      if (android_logger_list_read(logger_list_non_block2.get(), &log_msg) <= 0) {
         done2 = true;
       } else {
         ++count2;
       }
     }
-  } while ((!done1) || (!done2));
+  }
 
-  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);
+  EXPECT_EQ(expected_count1, count1);
+  EXPECT_EQ(expected_count2, count2);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#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) &&
@@ -1415,9 +1247,8 @@
 
   android_log_format_free(p_format);
 }
-#endif  // USING_LOGGER_DEFAULT
 
-#ifdef USING_LOGGER_DEFAULT  // Do not retest property handling
+#ifdef ENABLE_FLAKY_TESTS
 TEST(liblog, is_loggable) {
 #ifdef __ANDROID__
   static const char tag[] = "is_loggable";
@@ -1705,14 +1536,13 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // USING_LOGGER_DEFAULT
+#endif  // ENABLE_FLAKY_TESTS
 
+#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);
@@ -1747,19 +1577,17 @@
 
   return count;
 }
-#endif  // TEST_PREFIX
 
 TEST(liblog, enoent) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   if (getuid() != 0) {
     GTEST_SKIP() << "Skipping test, must be run as root.";
     return;
   }
 
-  TEST_PREFIX
   log_time ts(CLOCK_MONOTONIC);
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
-  EXPECT_EQ(SUPPORTS_END_TO_END, count_matching_ts(ts));
+  EXPECT_EQ(1, count_matching_ts(ts));
 
   // This call will fail unless we are root, beware of any
   // test prior to this one playing with setuid and causing interference.
@@ -1800,19 +1628,19 @@
 
   ts = log_time(CLOCK_MONOTONIC);
   EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
-  EXPECT_EQ(SUPPORTS_END_TO_END, count_matching_ts(ts));
+  EXPECT_EQ(1, count_matching_ts(ts));
 
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 #endif  // __ANDROID__
-#endif  // USING_LOGGER_DEFAULT
+#endif  // ENABLE_FLAKY_TESTS
 
 // 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";
@@ -2069,113 +1897,74 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // USING_LOGGER_DEFAULT
+#endif  // ENABLE_FLAKY_TESTS
 
-#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;
+#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);
+  };
 
-  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;
+  auto check_function = [&](log_msg log_msg, bool* found) {
+    char* event_data = log_msg.msg();
+    char* original = event_data;
 
     // Tag
-    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;
+    auto* event_header = reinterpret_cast<android_event_header_t*>(event_data);
+    event_data += sizeof(android_event_header_t);
+    if (event_header->tag != tag) {
+      return;
     }
 
     // List type
-    ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
-    eventData++;
-
-    // Number of elements in list
-    ASSERT_EQ(3, eventData[0]);
-    eventData++;
+    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);
 
     // Element #1: string type for subtag
-    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;
+    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;
     }
-    eventData += subtag_len;
+    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;
 
     // Element #2: int type for uid
-    ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
-    eventData++;
-
-    ASSERT_EQ(UID, (int)get4LE(eventData));
-    eventData += 4;
+    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);
 
     // Element #3: string type for data
-    ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-    eventData++;
-
-    size_t dataLen = get4LE(eventData);
-    eventData += 4;
-    if (DATA_LEN < 512) ASSERT_EQ(DATA_LEN, (int)dataLen);
-
-    if (memcmp(payload, eventData, dataLen)) {
-      continue;
+    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);
 
-    if (DATA_LEN >= 512) {
-      eventData += dataLen;
+    if (data_len >= 512) {
+      event_data += message_data_len;
       // 4 bytes for the tag, and max_payload_buf should be truncated.
-      ASSERT_LE(4 + 512, eventData - original);       // worst expectations
-      ASSERT_GT(4 + DATA_LEN, eventData - original);  // must be truncated
+      ASSERT_LE(4 + 512, event_data - original);       // worst expectations
+      ASSERT_GT(4 + data_len, event_data - original);  // must be truncated
     }
+    *found = true;
+  };
 
-    ++count;
-  }
-
-  android_logger_list_close(logger_list);
+  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
 }
 #endif
 
@@ -2189,11 +1978,8 @@
 #endif
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
-#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);
+#ifdef __ANDROID__
+  android_errorWriteWithInfoLog_helper(UNIQUE_TAG(1), "test-subtag", -1, max_payload_buf, 200);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2201,24 +1987,21 @@
 
 TEST(liblog,
      android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
-#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);
+#ifdef __ANDROID__
+  android_errorWriteWithInfoLog_helper(UNIQUE_TAG(2), "test-subtag", -1, max_payload_buf,
+                                       sizeof(max_payload_buf));
 #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 TEST_PREFIX
-  int count;
-  android_errorWriteWithInfoLog_helper(UNIQUE_TAG(3), "test-subtag", -1, NULL,
-                                       200, count);
-  EXPECT_EQ(0, count);
+#ifdef __ANDROID__
+  int retval_android_errorWriteWithinInfoLog =
+      android_errorWriteWithInfoLog(UNIQUE_TAG(3), "test-subtag", -1, nullptr, 200);
+  ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2226,12 +2009,9 @@
 
 TEST(liblog,
      android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
-#ifdef TEST_PREFIX
-  int count;
+#ifdef __ANDROID__
   android_errorWriteWithInfoLog_helper(
-      UNIQUE_TAG(4), "abcdefghijklmnopqrstuvwxyz now i know my abc", -1,
-      max_payload_buf, 200, count);
-  EXPECT_EQ(SUPPORTS_END_TO_END, count);
+      UNIQUE_TAG(4), "abcdefghijklmnopqrstuvwxyz now i know my abc", -1, max_payload_buf, 200);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2245,144 +2025,59 @@
   buf_write_test(max_payload_buf);
 }
 
-#ifdef TEST_PREFIX
-static void android_errorWriteLog_helper(int TAG, const char* SUBTAG,
-                                         int& count) {
-  TEST_PREFIX
-  struct logger_list* logger_list;
-
-  pid_t pid = getpid();
-
-  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
-    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
-    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
-    ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-    eventData++;
-
-    ASSERT_EQ(strlen(SUBTAG), get4LE(eventData));
-    eventData += 4;
-
-    if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
-      continue;
-    }
-    ++count;
-  }
-
-  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);
+#ifdef __ANDROID__
+  int kTag = UNIQUE_TAG(5);
+  const char* kSubTag = "test-subtag";
+
+  auto write_function = [&] {
+    int retval_android_errorWriteLog = android_errorWriteLog(kTag, kSubTag);
+    ASSERT_LT(0, retval_android_errorWriteLog);
+  };
+
+  auto check_function = [&](log_msg log_msg, bool* found) {
+    char* event_data = log_msg.msg();
+
+    // 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;
+    }
+
+    // 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);
+
+    // 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;
+    }
+  };
+
+  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
+
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
-#ifdef TEST_PREFIX
-  int count;
-  android_errorWriteLog_helper(UNIQUE_TAG(6), NULL, count);
-  EXPECT_EQ(0, count);
+#ifdef __ANDROID__
+  EXPECT_LT(android_errorWriteLog(UNIQUE_TAG(6), nullptr), 0);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
 
 // Do not retest logger list handling
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
 static int is_real_element(int type) {
   return ((type == EVENT_TYPE_INT) || (type == EVENT_TYPE_LONG) ||
           (type == EVENT_TYPE_STRING) || (type == EVENT_TYPE_FLOAT));
@@ -2537,9 +2232,9 @@
 
   return 0;
 }
-#endif  // TEST_PREFIX
+#endif  // __ANDROID__
 
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
 static const char* event_test_int32(uint32_t tag, size_t& expected_len) {
   android_log_context ctx;
 
@@ -2793,53 +2488,21 @@
 
 static void create_android_logger(const char* (*fn)(uint32_t tag,
                                                     size_t& expected_len)) {
-  TEST_PREFIX
-  struct logger_list* logger_list;
+  size_t expected_len;
+  const char* expected_string;
+  auto write_function = [&] {
+    expected_string = (*fn)(1005, expected_len);
+    ASSERT_NE(nullptr, expected_string);
+  };
 
   pid_t pid = getpid();
-
-  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;
+  auto check_function = [&](log_msg log_msg, bool* found) {
+    if (static_cast<size_t>(log_msg.entry.len) != expected_len) {
+      return;
     }
 
     char* eventData = log_msg.msg();
 
-    ++count;
-
     AndroidLogFormat* logformat = android_log_format_new();
     EXPECT_TRUE(NULL != logformat);
     AndroidLogEntry entry;
@@ -2862,29 +2525,28 @@
     // test buffer reading API
     int buffer_to_string = -1;
     if (eventData) {
-      snprintf(msgBuf, sizeof(msgBuf), "I/[%" PRIu32 "]", get4LE(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);
       print_barrier();
       fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
       memset(msgBuf, 0, sizeof(msgBuf));
-      buffer_to_string = android_log_buffer_to_string(
-          eventData + sizeof(uint32_t), log_msg.entry.len - sizeof(uint32_t),
-          msgBuf, sizeof(msgBuf));
+      buffer_to_string =
+          android_log_buffer_to_string(eventData, log_msg.entry.len, msgBuf, sizeof(msgBuf));
       fprintf(stderr, "%s\n", msgBuf);
       print_barrier();
     }
     EXPECT_EQ(0, buffer_to_string);
-    EXPECT_EQ(strlen(expected_string), strlen(msgBuf));
-    EXPECT_EQ(0, strcmp(expected_string, msgBuf));
-  }
+    EXPECT_STREQ(expected_string, msgBuf);
+    *found = true;
+  };
 
-  EXPECT_EQ(SUPPORTS_END_TO_END, count);
-
-  android_logger_list_close(logger_list);
+  RunLogTests(LOG_ID_EVENTS, write_function, check_function);
 }
 #endif
 
 TEST(liblog, create_android_logger_int32) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_int32);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2892,7 +2554,7 @@
 }
 
 TEST(liblog, create_android_logger_int64) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_int64);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2900,7 +2562,7 @@
 }
 
 TEST(liblog, create_android_logger_list_int64) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_list_int64);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2908,7 +2570,7 @@
 }
 
 TEST(liblog, create_android_logger_simple_automagic_list) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_simple_automagic_list);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2916,7 +2578,7 @@
 }
 
 TEST(liblog, create_android_logger_list_empty) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_list_empty);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2924,7 +2586,7 @@
 }
 
 TEST(liblog, create_android_logger_complex_nested_list) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_complex_nested_list);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2932,7 +2594,7 @@
 }
 
 TEST(liblog, create_android_logger_7_level_prefix) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_7_level_prefix);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2940,7 +2602,7 @@
 }
 
 TEST(liblog, create_android_logger_7_level_suffix) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_7_level_suffix);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2948,7 +2610,7 @@
 }
 
 TEST(liblog, create_android_logger_android_log_error_write) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   create_android_logger(event_test_android_log_error_write);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2956,14 +2618,13 @@
 }
 
 TEST(liblog, create_android_logger_android_log_error_write_null) {
-#ifdef TEST_PREFIX
+#ifdef __ANDROID__
   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;
 
@@ -2990,9 +2651,8 @@
   EXPECT_LE(0, android_log_destroy(&ctx));
   ASSERT_TRUE(NULL == ctx);
 }
-#endif  // USING_LOGGER_DEFAULT
 
-#ifdef USING_LOGGER_DEFAULT  // Do not retest pmsg functionality
+#ifdef ENABLE_FLAKY_TESTS
 #ifdef __ANDROID__
 #ifndef NO_PSTORE
 static const char __pmsg_file[] =
@@ -3070,7 +2730,7 @@
   EXPECT_EQ(ANDROID_LOG_VERBOSE, prio);
   EXPECT_FALSE(NULL == strstr(__pmsg_file, filename));
   EXPECT_EQ(len, sizeof(max_payload_buf));
-  EXPECT_EQ(0, strcmp(max_payload_buf, buf));
+  EXPECT_STREQ(max_payload_buf, buf);
 
   ++signaled;
   if ((len != sizeof(max_payload_buf)) || strcmp(max_payload_buf, buf)) {
@@ -3129,9 +2789,8 @@
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
-#endif  // USING_LOGGER_DEFAULT
+#endif  // ENABLE_FLAKY_TESTS
 
-#ifdef USING_LOGGER_DEFAULT  // Do not retest event mapping functionality
 TEST(liblog, android_lookupEventTagNum) {
 #ifdef __ANDROID__
   EventTagMap* map = android_openEventTagMap(NULL);
@@ -3148,4 +2807,3 @@
   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
deleted file mode 100644
index 2edea27..0000000
--- a/liblog/tests/liblog_test_default.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifdef __ANDROID__
-#include <log/log_transport.h>
-#define TEST_LOGGER LOGGER_DEFAULT
-#endif
-#define USING_LOGGER_DEFAULT
-#include "liblog_test.cpp"
diff --git a/liblog/tests/liblog_test_stderr.cpp b/liblog/tests/liblog_test_stderr.cpp
deleted file mode 100644
index abc1b9c..0000000
--- a/liblog/tests/liblog_test_stderr.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
-#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 443c3ea..1be99aa 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -29,54 +29,6 @@
 // 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 ebf0b15..e06964f 100644
--- a/liblog/tests/log_wrap_test.cpp
+++ b/liblog/tests/log_wrap_test.cpp
@@ -27,12 +27,9 @@
 #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;
@@ -61,60 +58,27 @@
 
   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();
 
-  // 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;
-    }
-  }
+  FAIL() << "read_with_wrap() should not return before the alarm is triggered.";
 
   alarm(0);
-  EXPECT_GT(timer.duration(), std::chrono::seconds(40));
+  sigaction(SIGALRM, &old_sigaction, nullptr);
 #else
   GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index f782ec5..8c1280f 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -48,6 +48,10 @@
     // 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.
     //
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 4c2be91..378a4cd 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -31,6 +31,7 @@
 #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;
@@ -100,6 +101,33 @@
     }
 }
 
+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();
@@ -334,7 +362,9 @@
     // Check for names
     EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
     EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
-    EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
+    EXPECT_TRUE(vmas[2].name == "[anon:libc_malloc]" ||
+                android::base::StartsWith(vmas[2].name, "[anon:scudo:"))
+            << "Unknown map name " << vmas[2].name;
     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]");
@@ -432,7 +462,9 @@
     // Check for names
     EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
     EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
-    EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
+    EXPECT_TRUE(vmas[2].name == "[anon:libc_malloc]" ||
+                android::base::StartsWith(vmas[2].name, "[anon:scudo:"))
+            << "Unknown map name " << vmas[2].name;
     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 6f68ab4..9e9a705 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -244,6 +244,15 @@
     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) {
     // 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
@@ -269,11 +278,8 @@
         return true;
     }
 
-    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;
+    ::android::base::unique_fd pagemap_fd(GetPagemapFd(pid_));
+    if (pagemap_fd == -1) {
         return false;
     }
 
@@ -290,6 +296,20 @@
     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/procmem.cpp b/libmeminfo/tools/procmem.cpp
index 47881ed..b245f2a 100644
--- a/libmeminfo/tools/procmem.cpp
+++ b/libmeminfo/tools/procmem.cpp
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <stdlib.h>
+#include <sys/mman.h>
 #include <unistd.h>
 
 #include <iostream>
@@ -59,25 +60,25 @@
 
 static void print_separator(std::stringstream& ss) {
     if (show_wss) {
-        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "-------",
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n",
                                             "-------", "-------", "-------", "-------", "-------",
-                                            "-------", "");
+                                            "-------", "-------", "-------", "");
         return;
     }
-    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  %7s  %7s  %s\n",
                                         "-------", "-------", "-------", "-------", "-------",
-                                        "-------", "-------", "");
+                                        "-------", "-------", "-------", "-------", "");
 }
 
 static void print_header(std::stringstream& ss) {
     if (show_wss) {
-        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "WRss",
+        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "WRss",
                                             "WPss", "WUss", "WShCl", "WShDi", "WPrCl", "WPrDi",
-                                            "Name");
+                                            "Flags", "Name");
     } else {
-        ss << ::android::base::StringPrintf("%7s  %7s  %7s  %7s  %7s  %7s  %7s  %7s  %s\n", "Vss",
-                                            "Rss", "Pss", "Uss", "ShCl", "ShDi", "PrCl", "PrDi",
-                                            "Name");
+        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");
     }
     print_separator(ss);
 }
@@ -103,7 +104,15 @@
             continue;
         }
         print_stats(ss, vma_stats);
-        ss << vma.name << std::endl;
+
+        // 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;
     }
     print_separator(ss);
     print_stats(ss, proc_stats);
diff --git a/libmeminfo/tools/showmap.cpp b/libmeminfo/tools/showmap.cpp
index 8ea2108..72ab0f3 100644
--- a/libmeminfo/tools/showmap.cpp
+++ b/libmeminfo/tools/showmap.cpp
@@ -18,6 +18,7 @@
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/mman.h>
 #include <sys/signal.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -139,6 +140,9 @@
     if (!g_verbose && !g_show_addr) {
         printf("   # ");
     }
+    if (g_verbose) {
+        printf(" flags ");
+    }
     printf(" object\n");
 }
 
@@ -150,6 +154,9 @@
     if (!g_verbose && !g_show_addr) {
         printf("---- ");
     }
+    if (g_verbose) {
+        printf("------ ");
+    }
     printf("------------------------------\n");
 }
 
@@ -169,6 +176,18 @@
     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) {
diff --git a/libmeminfo/vts/Android.bp b/libmeminfo/vts/Android.bp
index 5a3a23b..a92f669 100644
--- a/libmeminfo/vts/Android.bp
+++ b/libmeminfo/vts/Android.bp
@@ -12,9 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_test {
-    name: "vts_meminfo_test",
+cc_defaults {
+    name: "vts_meminfo_defaults",
     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/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index ce937fd..c4add19 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -25,6 +25,7 @@
 #include <unordered_map>
 
 #include <android-base/macros.h>
+#include <android-base/strings.h>
 #include <backtrace.h>
 
 #include "Allocator.h"
@@ -250,7 +251,8 @@
     } else if (mapping_name == current_lib) {
       // .rodata or .data section
       globals_mappings.emplace_back(*it);
-    } else if (mapping_name == "[anon:libc_malloc]") {
+    } else if (mapping_name == "[anon:libc_malloc]" ||
+               android::base::StartsWith(mapping_name, "[anon:scudo:")) {
       // named malloc mapping
       heap_mappings.emplace_back(*it);
     } else if (has_prefix(mapping_name, "[anon:dalvik-")) {
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
index 655e826..dc863c9 100644
--- a/libmemunreachable/ScopedDisableMalloc.h
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -34,8 +34,8 @@
 
   void Disable() {
     if (!disabled_) {
-      malloc_disable();
       disabled_ = true;
+      malloc_disable();
     }
   }
 
@@ -71,8 +71,7 @@
 
 class ScopedDisableMallocTimeout {
  public:
-  explicit ScopedDisableMallocTimeout(
-      std::chrono::milliseconds timeout = std::chrono::milliseconds(2000))
+  explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::seconds(10))
       : timeout_(timeout), timed_out_(false), disable_malloc_() {
     Disable();
   }
diff --git a/libnativebridge/.clang-format b/libnativebridge/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/libnativebridge/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
deleted file mode 100644
index 10d42e4..0000000
--- a/libnativebridge/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-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
deleted file mode 100644
index daf87f4..0000000
--- a/libnativebridge/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index e9c9500..0000000
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef 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
deleted file mode 100644
index a6841a3..0000000
--- a/libnativebridge/libnativebridge.map.txt
+++ /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.
-#
-
-# 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
deleted file mode 100644
index 9adba9a..0000000
--- a/libnativebridge/native_bridge.cc
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define 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
deleted file mode 100644
index 94c8084..0000000
--- a/libnativebridge/native_bridge_lazy.cc
+++ /dev/null
@@ -1,167 +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 "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
deleted file mode 100644
index 2bb8467..0000000
--- a/libnativebridge/tests/Android.bp
+++ /dev/null
@@ -1,110 +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_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
deleted file mode 100644
index 58270c4..0000000
--- a/libnativebridge/tests/CodeCacheCreate_test.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index 8ba0158..0000000
--- a/libnativebridge/tests/CodeCacheExists_test.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index 4ea519e..0000000
--- a/libnativebridge/tests/CodeCacheStatFail_test.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index b033792..0000000
--- a/libnativebridge/tests/CompleteFlow_test.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index b9894f6..0000000
--- a/libnativebridge/tests/DummyNativeBridge.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// 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
deleted file mode 100644
index 6920c74..0000000
--- a/libnativebridge/tests/DummyNativeBridge2.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// 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
deleted file mode 100644
index 4ef1c82..0000000
--- a/libnativebridge/tests/DummyNativeBridge3.cpp
+++ /dev/null
@@ -1,124 +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.
- */
-
-// 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
deleted file mode 100644
index 8f7973d..0000000
--- a/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index 44e45e3..0000000
--- a/libnativebridge/tests/NativeBridge2Signal_test.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index 668d942..0000000
--- a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp
+++ /dev/null
@@ -1,40 +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 "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
deleted file mode 100644
index 0b9f582..0000000
--- a/libnativebridge/tests/NativeBridge3GetError_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index b0d6b09..0000000
--- a/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index 325e40b..0000000
--- a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index 4caeb44..0000000
--- a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index 93a979c..0000000
--- a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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/libnativebridge/tests/NativeBridgeApi.c b/libnativebridge/tests/NativeBridgeApi.c
deleted file mode 100644
index 7ab71fe..0000000
--- a/libnativebridge/tests/NativeBridgeApi.c
+++ /dev/null
@@ -1,25 +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.
- */
-
-/* The main purpose of this test is to ensure this C header compiles in C, so
- * that no C++ features inadvertently leak into the C ABI. */
-#include "nativebridge/native_bridge.h"
-
-int main(int argc, char** argv) {
-  (void)argc;
-  (void)argv;
-  return 0;
-}
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
deleted file mode 100644
index 0f99816..0000000
--- a/libnativebridge/tests/NativeBridgeTest.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef 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
deleted file mode 100644
index d3f9a80..0000000
--- a/libnativebridge/tests/NativeBridgeVersion_test.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index c8ff743..0000000
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index 5a2b0a1..0000000
--- a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "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
deleted file mode 100644
index af976b1..0000000
--- a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <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
deleted file mode 100644
index cd5a8e2..0000000
--- a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <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/libnativebridge/tests/ReSetupNativeBridge_test.cpp b/libnativebridge/tests/ReSetupNativeBridge_test.cpp
deleted file mode 100644
index 944e5d7..0000000
--- a/libnativebridge/tests/ReSetupNativeBridge_test.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-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 android
diff --git a/libnativebridge/tests/UnavailableNativeBridge_test.cpp b/libnativebridge/tests/UnavailableNativeBridge_test.cpp
deleted file mode 100644
index ad374a5..0000000
--- a/libnativebridge/tests/UnavailableNativeBridge_test.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeBridgeTest.h"
-
-namespace android {
-
-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 android
diff --git a/libnativebridge/tests/ValidNameNativeBridge_test.cpp b/libnativebridge/tests/ValidNameNativeBridge_test.cpp
deleted file mode 100644
index 690be4a..0000000
--- a/libnativebridge/tests/ValidNameNativeBridge_test.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <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/libnativeloader/.clang-format b/libnativeloader/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/libnativeloader/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
deleted file mode 100644
index b860db9..0000000
--- a/libnativeloader/Android.bp
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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: {
-            srcs: [
-                "library_namespaces.cpp",
-                "native_loader_namespace.cpp",
-                "public_libraries.cpp",
-            ],
-            shared_libs: [
-                "libdl_android",
-            ],
-        },
-    },
-    required: [
-        "llndk.libraries.txt",
-        "vndksp.libraries.txt",
-    ],
-    stubs: {
-        symbol_file: "libnativeloader.map.txt",
-        versions: ["1"],
-    },
-}
-
-// TODO(b/124250621) eliminate the need for this library
-cc_library {
-    name: "libnativeloader_lazy",
-    defaults: ["libnativeloader-defaults"],
-    host_supported: false,
-    srcs: ["native_loader_lazy.cpp"],
-    required: ["libnativeloader"],
-}
-
-cc_library_headers {
-    name: "libnativeloader-headers",
-    host_supported: true,
-    export_include_dirs: ["include"],
-}
-
-// TODO(jiyong) Remove this when its use in the internal master is
-// switched to libnativeloader-headers
-cc_library_headers {
-    name: "libnativeloader-dummy-headers",
-    host_supported: true,
-    export_include_dirs: ["include"],
-}
-
-cc_test {
-    name: "libnativeloader_test",
-    srcs: [
-        "native_loader_test.cpp",
-        "native_loader.cpp",
-        "library_namespaces.cpp",
-        "native_loader_namespace.cpp",
-        "public_libraries.cpp",
-    ],
-    cflags: ["-DANDROID"],
-    static_libs: [
-        "libbase",
-        "liblog",
-        "libnativehelper",
-        "libgmock",
-    ],
-    header_libs: [
-        "libnativebridge-headers",
-        "libnativeloader-headers",
-    ],
-    system_shared_libs: ["libc", "libm"],
-    test_suites: ["device-tests"],
-}
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
deleted file mode 100644
index f735653..0000000
--- a/libnativeloader/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-dimitry@google.com
-jiyong@google.com
-ngeoffray@google.com
-oth@google.com
-mast@google.com
-rpl@google.com
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
deleted file mode 100644
index 46f6fdd..0000000
--- a/libnativeloader/README.md
+++ /dev/null
@@ -1,84 +0,0 @@
-libnativeloader
-===============================================================================
-
-Overview
--------------------------------------------------------------------------------
-libnativeloader is responsible for loading native shared libraries (`*.so`
-files) inside the Android Runtime (ART). The native shared libraries could be
-app-provided JNI libraries or public native libraries like `libc.so` provided
-by the platform.
-
-The most typical use case of this library is calling `System.loadLibrary(name)`.
-When the method is called, the ART runtime delegates the call to this library
-along with the reference to the classloader where the call was made.  Then this
-library finds the linker namespace (named `classloader-namespace`) that is
-associated with the given classloader, and tries to load the requested library
-from the namespace. The actual searching, loading, and linking of the library
-is performed by the dynamic linker.
-
-The linker namespace is created when an APK is loaded into the process, and is
-associated with the classloader that loaded the APK. The linker namespace is
-configured so that only the JNI libraries embedded in the APK is accessible
-from the namespace, thus preventing an APK from loading JNI libraries of other
-APKs.
-
-The linker namespace is also configured differently depending on other
-characteristics of the APK such as whether or not the APK is bundled with the
-platform. In case of the unbundled, i.e., downloaded or updated APK, only the
-public native libraries that is listed in `/system/etc/public.libraries.txt`
-are available from the platform, whereas in case of the bundled, all libraries
-under `/system/lib` are available (i.e. shared). In case when the unbundled
-app is from `/vendor` or `/product` partition, the app is additionally provided
-with the [VNDK-SP](https://source.android.com/devices/architecture/vndk#sp-hal)
-libraries. As the platform is getting modularized with
-[APEX](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md),
-some libraries are no longer provided from platform, but from the APEXes which
-have their own linker namespaces. For example, ICU libraries `libicuuc.so` and
-`libicui18n.so` are from the runtime APEX.
-
-The list of public native libraries is not static. The default set of libraries
-are defined in AOSP, but partners can extend it to include their own libraries.
-Currently, following extensions are available:
-
-- `/vendor/etc/public.libraries.txt`: libraries in `/vendor/lib` that are
-specific to the underlying SoC, e.g. GPU, DSP, etc.
-- `/{system|product}/etc/public.libraries-<companyname>.txt`: libraries in
-`/{system|system}/lib` that a device manufacturer has newly added. The
-libraries should be named as `lib<name>.<companyname>.so` as in
-`libFoo.acme.so`.
-
-Note that, due to the naming constraint requiring `.<companyname>.so` suffix, it
-is prohibited for a device manufacturer to expose an AOSP-defined private
-library, e.g. libgui.so, libart.so, etc., to APKs.
-
-Lastly, libnativeloader is responsible for abstracting the two types of the
-dynamic linker interface: `libdl.so` and `libnativebridge.so`. The former is
-for non-translated, e.g. ARM-on-ARM, libraries, while the latter is for
-loading libraries in a translated environment such as ARM-on-x86.
-
-Implementation
--------------------------------------------------------------------------------
-Implementation wise, libnativeloader consists of four parts:
-
-- `native_loader.cpp`
-- `library_namespaces.cpp`
-- `native_loader_namespace.cpp`
-- `public_libraries.cpp`
-
-`native_loader.cpp` implements the public interface of this library. It is just
-a thin wrapper around `library_namespaces.cpp` and `native_loader_namespace.cpp`.
-
-`library_namespaces.cpp` implements the singleton class `LibraryNamespaces` which
-is a manager-like entity that is responsible for creating and configuring
-linker namespaces and finding an already created linker namespace for a given
-classloader.
-
-`native_loader_namesapces.cpp` implements the class `NativeLoaderNamespace` that
-models a linker namespace. It's main job is to abstract the two types of the
-dynamic linker interface so that other parts of this library do not have to know
-the differences of the interfaces.
-
-`public_libraries.cpp` is responsible for reading `*.txt` files for the public
-native libraries from the various partitions. It can be considered as a part of
-`LibraryNamespaces` but is separated from it to hide the details of the parsing
-routines.
diff --git a/libnativeloader/TEST_MAPPING b/libnativeloader/TEST_MAPPING
deleted file mode 100644
index 7becb77..0000000
--- a/libnativeloader/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "libnativeloader_test"
-    }
-  ],
-  "imports": [
-    {
-      "path": "cts/tests/tests/jni"
-    }
-  ]
-}
diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
deleted file mode 100644
index 8937636..0000000
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __ANDROID_DLEXT_NAMESPACES_H__
-#define __ANDROID_DLEXT_NAMESPACES_H__
-
-#include <android/dlext.h>
-#include <stdbool.h>
-
-__BEGIN_DECLS
-
-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,
-
-  /* This flag instructs linker to use this namespace as the 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. There can
-   * be only one anonymous namespace in a process. If there already is an
-   * anonymous namespace in the process, using this flag when creating a new
-   * namespace causes an error.
-   */
-  ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS = 0x10000000,
-
-  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
deleted file mode 100644
index 51fb875..0000000
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef 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
deleted file mode 100644
index 40c30bd..0000000
--- a/libnativeloader/libnativeloader.map.txt
+++ /dev/null
@@ -1,31 +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.
-#
-
-# TODO(b/122710865): Prune these uses once the runtime APEX is complete.
-LIBNATIVELOADER_1 {
-  global:
-    OpenNativeLibrary;
-    InitializeNativeLoader;
-    ResetNativeLoader;
-    CloseNativeLibrary;
-    OpenNativeLibraryInNamespace;
-    FindNamespaceByClassLoader;
-    FindNativeLoaderNamespaceByClassLoader;
-    CreateClassLoaderNamespace;
-    NativeLoaderFreeErrorMessage;
-  local:
-    *;
-};
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
deleted file mode 100644
index 9a33b55..0000000
--- a/libnativeloader/library_namespaces.cpp
+++ /dev/null
@@ -1,319 +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 "library_namespaces.h"
-
-#include <dirent.h>
-#include <dlfcn.h>
-
-#include <regex>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/macros.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <nativehelper/ScopedUtfChars.h>
-
-#include "nativeloader/dlext_namespaces.h"
-#include "public_libraries.h"
-#include "utils.h"
-
-using android::base::Error;
-
-namespace android::nativeloader {
-
-namespace {
-// The device may be configured to have the vendor libraries loaded to a separate namespace.
-// For historical reasons this namespace was named sphal but effectively it is intended
-// to use to load vendor libraries to separate namespace with controlled interface between
-// vendor and system namespaces.
-constexpr const char* kVendorNamespaceName = "sphal";
-constexpr const char* kVndkNamespaceName = "vndk";
-constexpr const char* kRuntimeNamespaceName = "runtime";
-constexpr const char* kNeuralNetworksNamespaceName = "neuralnetworks";
-
-// classloader-namespace is a linker namespace that is created for the loaded
-// app. To be specific, it is created for the app classloader. When
-// System.load() is called from a Java class that is loaded from the
-// classloader, the classloader-namespace namespace associated with that
-// classloader is selected for dlopen. The namespace is configured so that its
-// search path is set to the app-local JNI directory and it is linked to the
-// platform namespace with the names of libs listed in the public.libraries.txt.
-// This way an app can only load its own JNI libraries along with the public libs.
-constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-// Same thing for vendor APKs.
-constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-
-// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
-// System.load() with an absolute path which is outside of the classloader library search path.
-// This list includes all directories app is allowed to access this way.
-constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";
-
-constexpr const char* kVendorLibPath = "/vendor/" LIB;
-constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
-
-const std::regex kVendorDexPathRegex("(^|:)/vendor/");
-const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
-
-// Define origin of APK if it is from vendor partition or product partition
-typedef enum {
-  APK_ORIGIN_DEFAULT = 0,
-  APK_ORIGIN_VENDOR = 1,
-  APK_ORIGIN_PRODUCT = 2,
-} ApkOrigin;
-
-jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
-  jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
-  jmethodID get_parent =
-      env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;");
-
-  return env->CallObjectMethod(class_loader, get_parent);
-}
-
-ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) {
-  ApkOrigin apk_origin = APK_ORIGIN_DEFAULT;
-
-  if (dex_path != nullptr) {
-    ScopedUtfChars dex_path_utf_chars(env, dex_path);
-
-    if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) {
-      apk_origin = APK_ORIGIN_VENDOR;
-    }
-
-    if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) {
-      LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR,
-                          "Dex path contains both vendor and product partition : %s",
-                          dex_path_utf_chars.c_str());
-
-      apk_origin = APK_ORIGIN_PRODUCT;
-    }
-  }
-  return apk_origin;
-}
-
-}  // namespace
-
-void LibraryNamespaces::Initialize() {
-  // Once public namespace is initialized there is no
-  // point in running this code - it will have no effect
-  // on the current list of public libraries.
-  if (initialized_) {
-    return;
-  }
-
-  // android_init_namespaces() expects all the public libraries
-  // to be loaded so that they can be found by soname alone.
-  //
-  // TODO(dimitry): this is a bit misleading since we do not know
-  // if the vendor public library is going to be opened from /vendor/lib
-  // we might as well end up loading them from /system/lib or /product/lib
-  // For now we rely on CTS test to catch things like this but
-  // it should probably be addressed in the future.
-  for (const auto& soname : android::base::Split(preloadable_public_libraries(), ":")) {
-    LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
-                        "Error preloading public library %s: %s", soname.c_str(), dlerror());
-  }
-}
-
-Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
-                                                         jobject class_loader, bool is_shared,
-                                                         jstring dex_path,
-                                                         jstring java_library_path,
-                                                         jstring java_permitted_path) {
-  std::string 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();
-    }
-  }
-
-  LOG_ALWAYS_FATAL_IF(FindNamespaceByClassLoader(env, class_loader) != nullptr,
-                      "There is already a namespace associated with this classloader");
-
-  std::string system_exposed_libraries = default_public_libraries();
-  const char* namespace_name = kClassloaderNamespaceName;
-  bool unbundled_vendor_or_product_app = false;
-  if ((apk_origin == APK_ORIGIN_VENDOR ||
-       (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
-      !is_shared) {
-    unbundled_vendor_or_product_app = true;
-    // For vendor / product apks, give access to the vendor / product lib even though
-    // they are treated as unbundled; the libs and apks are still bundled
-    // together in the vendor / product partition.
-    const char* origin_partition;
-    const char* origin_lib_path;
-
-    switch (apk_origin) {
-      case APK_ORIGIN_VENDOR:
-        origin_partition = "vendor";
-        origin_lib_path = kVendorLibPath;
-        break;
-      case APK_ORIGIN_PRODUCT:
-        origin_partition = "product";
-        origin_lib_path = kProductLibPath;
-        break;
-      default:
-        origin_partition = "unknown";
-        origin_lib_path = "";
-    }
-    library_path = library_path + ":" + origin_lib_path;
-    permitted_path = permitted_path + ":" + origin_lib_path;
-
-    // Also give access to LLNDK libraries since they are available to vendors
-    system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries().c_str();
-
-    // Different name is useful for debugging
-    namespace_name = kVendorClassloaderNamespaceName;
-    ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
-          origin_partition, library_path.c_str());
-  } else {
-    // extended public libraries are NOT available to vendor apks, otherwise it
-    // would be system->vendor violation.
-    if (!extended_public_libraries().empty()) {
-      system_exposed_libraries = system_exposed_libraries + ':' + extended_public_libraries();
-    }
-  }
-
-  // Create the app namespace
-  NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
-  // Heuristic: the first classloader with non-empty library_path is assumed to
-  // be the main classloader for app
-  // TODO(b/139178525) remove this heuristic by determining this in LoadedApk (or its
-  // friends) and then passing it down to here.
-  bool is_main_classloader = app_main_namespace_ == nullptr && !library_path.empty();
-  // Policy: the namespace for the main classloader is also used as the
-  // anonymous namespace.
-  bool also_used_as_anonymous = is_main_classloader;
-  // Note: this function is executed with g_namespaces_mutex held, thus no
-  // racing here.
-  auto app_ns = NativeLoaderNamespace::Create(
-      namespace_name, library_path, permitted_path, parent_ns, is_shared,
-      target_sdk_version < 24 /* is_greylist_enabled */, also_used_as_anonymous);
-  if (!app_ns) {
-    return app_ns.error();
-  }
-  // ... and link to other namespaces to allow access to some public libraries
-  bool is_bridged = app_ns->IsBridged();
-
-  auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged);
-  if (!platform_ns) {
-    return platform_ns.error();
-  }
-
-  auto linked = app_ns->Link(*platform_ns, system_exposed_libraries);
-  if (!linked) {
-    return linked.error();
-  }
-
-  auto runtime_ns = NativeLoaderNamespace::GetExportedNamespace(kRuntimeNamespaceName, is_bridged);
-  // Runtime apex does not exist in host, and under certain build conditions.
-  if (runtime_ns) {
-    linked = app_ns->Link(*runtime_ns, runtime_public_libraries());
-    if (!linked) {
-      return linked.error();
-    }
-  }
-
-  // Give access to NNAPI libraries (apex-updated LLNDK library).
-  auto nnapi_ns =
-      NativeLoaderNamespace::GetExportedNamespace(kNeuralNetworksNamespaceName, is_bridged);
-  if (nnapi_ns) {
-    linked = app_ns->Link(*nnapi_ns, neuralnetworks_public_libraries());
-    if (!linked) {
-      return linked.error();
-    }
-  }
-
-  // Give access to VNDK-SP libraries from the 'vndk' namespace.
-  if (unbundled_vendor_or_product_app && !vndksp_libraries().empty()) {
-    auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged);
-    if (vndk_ns) {
-      linked = app_ns->Link(*vndk_ns, vndksp_libraries());
-      if (!linked) {
-        return linked.error();
-      }
-    }
-  }
-
-  if (!vendor_public_libraries().empty()) {
-    auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
-    // when vendor_ns is not configured, link to the platform namespace
-    auto target_ns = vendor_ns ? vendor_ns : platform_ns;
-    if (target_ns) {
-      linked = app_ns->Link(*target_ns, vendor_public_libraries());
-      if (!linked) {
-        return linked.error();
-      }
-    }
-  }
-
-  namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), *app_ns));
-  if (is_main_classloader) {
-    app_main_namespace_ = &(*app_ns);
-  }
-
-  return &(namespaces_.back().second);
-}
-
-NativeLoaderNamespace* LibraryNamespaces::FindNamespaceByClassLoader(JNIEnv* env,
-                                                                     jobject class_loader) {
-  auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
-                         [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
-                           return env->IsSameObject(value.first, class_loader);
-                         });
-  if (it != namespaces_.end()) {
-    return &it->second;
-  }
-
-  return nullptr;
-}
-
-NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
-                                                                           jobject class_loader) {
-  jobject parent_class_loader = GetParentClassLoader(env, class_loader);
-
-  while (parent_class_loader != nullptr) {
-    NativeLoaderNamespace* ns;
-    if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) {
-      return ns;
-    }
-
-    parent_class_loader = GetParentClassLoader(env, parent_class_loader);
-  }
-
-  return nullptr;
-}
-
-}  // namespace android::nativeloader
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
deleted file mode 100644
index 7b3efff..0000000
--- a/libnativeloader/library_namespaces.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
-#if !defined(__ANDROID__)
-#error "Not available for host"
-#endif
-
-#define LOG_TAG "nativeloader"
-
-#include "native_loader_namespace.h"
-
-#include <list>
-#include <string>
-
-#include <android-base/result.h>
-#include <jni.h>
-
-namespace android::nativeloader {
-
-using android::base::Result;
-
-// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
-// objects for an app process. Its main job is to create (and configure) a new
-// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
-// object for a given ClassLoader.
-class LibraryNamespaces {
- public:
-  LibraryNamespaces() : initialized_(false), app_main_namespace_(nullptr) {}
-
-  LibraryNamespaces(LibraryNamespaces&&) = default;
-  LibraryNamespaces(const LibraryNamespaces&) = delete;
-  LibraryNamespaces& operator=(const LibraryNamespaces&) = delete;
-
-  void Initialize();
-  void Reset() {
-    namespaces_.clear();
-    initialized_ = false;
-    app_main_namespace_ = nullptr;
-  }
-  Result<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);
-  NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
-
- private:
-  Result<void> InitPublicNamespace(const char* library_path);
-  NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
-
-  bool initialized_;
-  NativeLoaderNamespace* app_main_namespace_;
-  std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_;
-};
-
-}  // namespace android::nativeloader
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
deleted file mode 100644
index 60d462f..0000000
--- a/libnativeloader/native_loader.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "nativeloader"
-
-#include "nativeloader/native_loader.h"
-
-#include <dlfcn.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <mutex>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <android-base/strings.h>
-#include <nativebridge/native_bridge.h>
-#include <nativehelper/ScopedUtfChars.h>
-
-#ifdef __ANDROID__
-#include <log/log.h>
-#include "library_namespaces.h"
-#include "nativeloader/dlext_namespaces.h"
-#endif
-
-namespace android {
-
-namespace {
-#if defined(__ANDROID__)
-using android::nativeloader::LibraryNamespaces;
-
-constexpr const char* kApexPath = "/apex/";
-
-std::mutex g_namespaces_mutex;
-LibraryNamespaces* g_namespaces = new LibraryNamespaces;
-
-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);
-    // TODO(b/139408016): Rename the runtime namespace to "art".
-    if (name == "art") {
-      name = "runtime";
-    }
-    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  // #if defined(__ANDROID__)
-}  // namespace
-
-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);
-  auto ns = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
-                                 library_path, permitted_path);
-  if (!ns) {
-    return env->NewStringUTF(ns.error().message().c_str());
-  }
-#else
-  UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path);
-#endif
-  return nullptr;
-}
-
-void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, 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.
-    Result<NativeLoaderNamespace*> isolated_ns =
-        g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */, nullptr,
-                             library_path, nullptr);
-    if (!isolated_ns) {
-      *error_msg = strdup(isolated_ns.error().message().c_str());
-      return nullptr;
-    } else {
-      ns = *isolated_ns;
-    }
-  }
-
-  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) {
-  auto handle = ns->Load(path);
-  if (!handle && error_msg != nullptr) {
-    *error_msg = strdup(handle.error().message().c_str());
-  }
-  if (needs_native_bridge != nullptr) {
-    *needs_native_bridge = ns->IsBridged();
-  }
-  return handle ? *handle : nullptr;
-}
-
-// 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 && !ns->IsBridged()) {
-    return ns->ToRawAndroidNamespace();
-  }
-  return nullptr;
-}
-
-NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
-  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
-}
-#endif
-
-};  // namespace android
diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp
deleted file mode 100644
index 2eb1203..0000000
--- a/libnativeloader/native_loader_lazy.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 "nativeloader/native_loader.h"
-#define LOG_TAG "nativeloader"
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <string.h>
-
-#include <log/log.h>
-
-namespace android {
-
-namespace {
-
-void* GetLibHandle() {
-  static void* handle = dlopen("libnativeloader.so", RTLD_NOW);
-  LOG_FATAL_IF(handle == nullptr, "Failed to load libnativeloader.so: %s", dlerror());
-  return handle;
-}
-
-template <typename FuncPtr>
-FuncPtr GetFuncPtr(const char* function_name) {
-  auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
-  LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
-  return f;
-}
-
-#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name)
-
-}  // namespace
-
-void InitializeNativeLoader() {
-  static auto f = GET_FUNC_PTR(InitializeNativeLoader);
-  return f();
-}
-
-jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader,
-                                   bool is_shared, jstring dex_path, jstring library_path,
-                                   jstring permitted_path) {
-  static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace);
-  return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path,
-           permitted_path);
-}
-
-void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, const char* caller_location, jstring library_path,
-                        bool* needs_native_bridge, char** error_msg) {
-  static auto f = GET_FUNC_PTR(OpenNativeLibrary);
-  return f(env, target_sdk_version, path, class_loader, caller_location, library_path,
-           needs_native_bridge, error_msg);
-}
-
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
-  static auto f = GET_FUNC_PTR(CloseNativeLibrary);
-  return f(handle, needs_native_bridge, error_msg);
-}
-
-void NativeLoaderFreeErrorMessage(char* msg) {
-  static auto f = GET_FUNC_PTR(NativeLoaderFreeErrorMessage);
-  return f(msg);
-}
-
-struct android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
-  static auto f = GET_FUNC_PTR(FindNamespaceByClassLoader);
-  return f(env, class_loader);
-}
-
-struct NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env,
-                                                                     jobject class_loader) {
-  static auto f = GET_FUNC_PTR(FindNativeLoaderNamespaceByClassLoader);
-  return f(env, class_loader);
-}
-
-void* OpenNativeLibraryInNamespace(struct NativeLoaderNamespace* ns, const char* path,
-                                   bool* needs_native_bridge, char** error_msg) {
-  static auto f = GET_FUNC_PTR(OpenNativeLibraryInNamespace);
-  return f(ns, path, needs_native_bridge, error_msg);
-}
-
-void ResetNativeLoader() {
-  static auto f = GET_FUNC_PTR(ResetNativeLoader);
-  return f();
-}
-
-#undef GET_FUNC_PTR
-
-}  // namespace android
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
deleted file mode 100644
index a81fddf..0000000
--- a/libnativeloader/native_loader_namespace.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.
- */
-
-#define LOG_TAG "nativeloader"
-
-#include "native_loader_namespace.h"
-
-#include <dlfcn.h>
-
-#include <functional>
-
-#include <android-base/strings.h>
-#include <log/log.h>
-#include <nativebridge/native_bridge.h>
-
-#include "nativeloader/dlext_namespaces.h"
-
-using android::base::Error;
-using android::base::Errorf;
-
-namespace android {
-
-namespace {
-
-constexpr const char* kDefaultNamespaceName = "default";
-constexpr const char* kPlatformNamespaceName = "platform";
-
-std::string GetLinkerError(bool is_bridged) {
-  const char* msg = is_bridged ? NativeBridgeGetError() : dlerror();
-  if (msg == nullptr) {
-    return "no error";
-  }
-  return std::string(msg);
-}
-
-}  // namespace
-
-Result<NativeLoaderNamespace> NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
-                                                                          bool is_bridged) {
-  if (!is_bridged) {
-    auto raw = android_get_exported_namespace(name.c_str());
-    if (raw != nullptr) {
-      return NativeLoaderNamespace(name, raw);
-    }
-  } else {
-    auto raw = NativeBridgeGetExportedNamespace(name.c_str());
-    if (raw != nullptr) {
-      return NativeLoaderNamespace(name, raw);
-    }
-  }
-  return Errorf("namespace {} does not exist or exported", name);
-}
-
-// The platform namespace is called "default" for binaries in /system and
-// "platform" for those in the Runtime APEX. Try "platform" first since
-// "default" always exists.
-Result<NativeLoaderNamespace> NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
-  auto ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
-  if (ns) return ns;
-  ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
-  if (ns) return ns;
-
-  // If nothing is found, return NativeLoaderNamespace constructed from nullptr.
-  // nullptr also means default namespace to the linker.
-  if (!is_bridged) {
-    return NativeLoaderNamespace(kDefaultNamespaceName, static_cast<android_namespace_t*>(nullptr));
-  } else {
-    return NativeLoaderNamespace(kDefaultNamespaceName,
-                                 static_cast<native_bridge_namespace_t*>(nullptr));
-  }
-}
-
-Result<NativeLoaderNamespace> NativeLoaderNamespace::Create(
-    const std::string& name, const std::string& search_paths, const std::string& permitted_paths,
-    const NativeLoaderNamespace* parent, bool is_shared, bool is_greylist_enabled,
-    bool also_used_as_anonymous) {
-  bool is_bridged = false;
-  if (parent != nullptr) {
-    is_bridged = parent->IsBridged();
-  } else if (!search_paths.empty()) {
-    is_bridged = NativeBridgeIsPathSupported(search_paths.c_str());
-  }
-
-  // Fall back to the platform namespace if no parent is set.
-  auto platform_ns = GetPlatformNamespace(is_bridged);
-  if (!platform_ns) {
-    return platform_ns.error();
-  }
-  const NativeLoaderNamespace& effective_parent = parent != nullptr ? *parent : *platform_ns;
-
-  // All namespaces for apps are isolated
-  uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
-
-  // The namespace is also used as the anonymous namespace
-  // which is used when the linker fails to determine the caller address
-  if (also_used_as_anonymous) {
-    type |= ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
-  }
-
-  // Bundled apps have access to all system libraries that are currently loaded
-  // in the default namespace
-  if (is_shared) {
-    type |= ANDROID_NAMESPACE_TYPE_SHARED;
-  }
-  if (is_greylist_enabled) {
-    type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
-  }
-
-  if (!is_bridged) {
-    android_namespace_t* raw =
-        android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
-                                 permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
-    if (raw != nullptr) {
-      return NativeLoaderNamespace(name, raw);
-    }
-  } else {
-    native_bridge_namespace_t* raw = NativeBridgeCreateNamespace(
-        name.c_str(), nullptr, search_paths.c_str(), type, permitted_paths.c_str(),
-        effective_parent.ToRawNativeBridgeNamespace());
-    if (raw != nullptr) {
-      return NativeLoaderNamespace(name, raw);
-    }
-  }
-  return Errorf("failed to create {} namespace name:{}, search_paths:{}, permitted_paths:{}",
-                is_bridged ? "bridged" : "native", name, search_paths, permitted_paths);
-}
-
-Result<void> NativeLoaderNamespace::Link(const NativeLoaderNamespace& target,
-                                         const std::string& shared_libs) const {
-  LOG_ALWAYS_FATAL_IF(shared_libs.empty(), "empty share lib when linking %s to %s",
-                      this->name().c_str(), target.name().c_str());
-  if (!IsBridged()) {
-    if (android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
-                                shared_libs.c_str())) {
-      return {};
-    }
-  } else {
-    if (NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
-                                   target.ToRawNativeBridgeNamespace(), shared_libs.c_str())) {
-      return {};
-    }
-  }
-  return Error() << GetLinkerError(IsBridged());
-}
-
-Result<void*> NativeLoaderNamespace::Load(const char* lib_name) const {
-  if (!IsBridged()) {
-    android_dlextinfo extinfo;
-    extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
-    extinfo.library_namespace = this->ToRawAndroidNamespace();
-    void* handle = android_dlopen_ext(lib_name, RTLD_NOW, &extinfo);
-    if (handle != nullptr) {
-      return handle;
-    }
-  } else {
-    void* handle =
-        NativeBridgeLoadLibraryExt(lib_name, RTLD_NOW, this->ToRawNativeBridgeNamespace());
-    if (handle != nullptr) {
-      return handle;
-    }
-  }
-  return Error() << GetLinkerError(IsBridged());
-}
-
-}  // namespace android
diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h
deleted file mode 100644
index 7200ee7..0000000
--- a/libnativeloader/native_loader_namespace.h
+++ /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.
- */
-#pragma once
-#if defined(__ANDROID__)
-
-#include <string>
-#include <variant>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/result.h>
-#include <android/dlext.h>
-#include <log/log.h>
-#include <nativebridge/native_bridge.h>
-
-namespace android {
-
-using android::base::Result;
-
-// NativeLoaderNamespace abstracts a linker namespace for the native
-// architecture (ex: arm on arm) or the translated architecture (ex: arm on
-// x86). Instances of this class are managed by LibraryNamespaces object.
-struct NativeLoaderNamespace {
- public:
-  static Result<NativeLoaderNamespace> Create(const std::string& name,
-                                              const std::string& search_paths,
-                                              const std::string& permitted_paths,
-                                              const NativeLoaderNamespace* parent, bool is_shared,
-                                              bool is_greylist_enabled,
-                                              bool also_used_as_anonymous);
-
-  NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
-  NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
-  NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
-
-  android_namespace_t* ToRawAndroidNamespace() const { return std::get<0>(raw_); }
-  native_bridge_namespace_t* ToRawNativeBridgeNamespace() const { return std::get<1>(raw_); }
-
-  std::string name() const { return name_; }
-  bool IsBridged() const { return raw_.index() == 1; }
-
-  Result<void> Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
-  Result<void*> Load(const char* lib_name) const;
-
-  static Result<NativeLoaderNamespace> GetExportedNamespace(const std::string& name,
-                                                            bool is_bridged);
-  static Result<NativeLoaderNamespace> GetPlatformNamespace(bool is_bridged);
-
- private:
-  explicit NativeLoaderNamespace(const std::string& name, android_namespace_t* ns)
-      : name_(name), raw_(ns) {}
-  explicit NativeLoaderNamespace(const std::string& name, native_bridge_namespace_t* ns)
-      : name_(name), raw_(ns) {}
-
-  std::string name_;
-  std::variant<android_namespace_t*, native_bridge_namespace_t*> raw_;
-};
-
-}  // namespace android
-#endif  // #if defined(__ANDROID__)
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
deleted file mode 100644
index 75255b6..0000000
--- a/libnativeloader/native_loader_test.cpp
+++ /dev/null
@@ -1,663 +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 <dlfcn.h>
-#include <memory>
-#include <unordered_map>
-
-#include <android-base/strings.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <jni.h>
-
-#include "native_loader_namespace.h"
-#include "nativeloader/dlext_namespaces.h"
-#include "nativeloader/native_loader.h"
-#include "public_libraries.h"
-
-using namespace ::testing;
-using namespace ::android::nativeloader::internal;
-
-namespace android {
-namespace nativeloader {
-
-// gmock interface that represents interested platform APIs on libdl and libnativebridge
-class Platform {
- public:
-  virtual ~Platform() {}
-
-  // libdl APIs
-  virtual void* dlopen(const char* filename, int flags) = 0;
-  virtual int dlclose(void* handle) = 0;
-  virtual char* dlerror(void) = 0;
-
-  // These mock_* are the APIs semantically the same across libdl and libnativebridge.
-  // Instead of having two set of mock APIs for the two, define only one set with an additional
-  // argument 'bool bridged' to identify the context (i.e., called for libdl or libnativebridge).
-  typedef char* mock_namespace_handle;
-  virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
-                                             const char* search_paths) = 0;
-  virtual mock_namespace_handle mock_create_namespace(
-      bool bridged, const char* name, const char* ld_library_path, const char* default_library_path,
-      uint64_t type, const char* permitted_when_isolated_path, mock_namespace_handle parent) = 0;
-  virtual bool mock_link_namespaces(bool bridged, mock_namespace_handle from,
-                                    mock_namespace_handle to, const char* sonames) = 0;
-  virtual mock_namespace_handle mock_get_exported_namespace(bool bridged, const char* name) = 0;
-  virtual void* mock_dlopen_ext(bool bridged, const char* filename, int flags,
-                                mock_namespace_handle ns) = 0;
-
-  // libnativebridge APIs for which libdl has no corresponding APIs
-  virtual bool NativeBridgeInitialized() = 0;
-  virtual const char* NativeBridgeGetError() = 0;
-  virtual bool NativeBridgeIsPathSupported(const char*) = 0;
-  virtual bool NativeBridgeIsSupported(const char*) = 0;
-
-  // To mock "ClassLoader Object.getParent()"
-  virtual const char* JniObject_getParent(const char*) = 0;
-};
-
-// The mock does not actually create a namespace object. But simply casts the pointer to the
-// string for the namespace name as the handle to the namespace object.
-#define TO_ANDROID_NAMESPACE(str) \
-  reinterpret_cast<struct android_namespace_t*>(const_cast<char*>(str))
-
-#define TO_BRIDGED_NAMESPACE(str) \
-  reinterpret_cast<struct native_bridge_namespace_t*>(const_cast<char*>(str))
-
-#define TO_MOCK_NAMESPACE(ns) reinterpret_cast<Platform::mock_namespace_handle>(ns)
-
-// These represents built-in namespaces created by the linker according to ld.config.txt
-static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = {
-    {"platform", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("platform"))},
-    {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))},
-    {"runtime", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("runtime"))},
-    {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))},
-    {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))},
-    {"neuralnetworks", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("neuralnetworks"))},
-};
-
-// The actual gmock object
-class MockPlatform : public Platform {
- public:
-  MockPlatform(bool is_bridged) : is_bridged_(is_bridged) {
-    ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_));
-    ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_));
-    ON_CALL(*this, mock_get_exported_namespace(_, _))
-        .WillByDefault(Invoke([](bool, const char* name) -> mock_namespace_handle {
-          if (namespaces.find(name) != namespaces.end()) {
-            return namespaces[name];
-          }
-          return TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("(namespace not found"));
-        }));
-  }
-
-  // Mocking libdl APIs
-  MOCK_METHOD2(dlopen, void*(const char*, int));
-  MOCK_METHOD1(dlclose, int(void*));
-  MOCK_METHOD0(dlerror, char*());
-
-  // Mocking the common APIs
-  MOCK_METHOD3(mock_init_anonymous_namespace, bool(bool, const char*, const char*));
-  MOCK_METHOD7(mock_create_namespace,
-               mock_namespace_handle(bool, const char*, const char*, const char*, uint64_t,
-                                     const char*, mock_namespace_handle));
-  MOCK_METHOD4(mock_link_namespaces,
-               bool(bool, mock_namespace_handle, mock_namespace_handle, const char*));
-  MOCK_METHOD2(mock_get_exported_namespace, mock_namespace_handle(bool, const char*));
-  MOCK_METHOD4(mock_dlopen_ext, void*(bool, const char*, int, mock_namespace_handle));
-
-  // Mocking libnativebridge APIs
-  MOCK_METHOD0(NativeBridgeInitialized, bool());
-  MOCK_METHOD0(NativeBridgeGetError, const char*());
-  MOCK_METHOD1(NativeBridgeIsPathSupported, bool(const char*));
-  MOCK_METHOD1(NativeBridgeIsSupported, bool(const char*));
-
-  // Mocking "ClassLoader Object.getParent()"
-  MOCK_METHOD1(JniObject_getParent, const char*(const char*));
-
- private:
-  bool is_bridged_;
-};
-
-static std::unique_ptr<MockPlatform> mock;
-
-// Provide C wrappers for the mock object.
-extern "C" {
-void* dlopen(const char* file, int flag) {
-  return mock->dlopen(file, flag);
-}
-
-int dlclose(void* handle) {
-  return mock->dlclose(handle);
-}
-
-char* dlerror(void) {
-  return mock->dlerror();
-}
-
-bool android_init_anonymous_namespace(const char* sonames, const char* search_path) {
-  return mock->mock_init_anonymous_namespace(false, sonames, search_path);
-}
-
-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) {
-  return TO_ANDROID_NAMESPACE(
-      mock->mock_create_namespace(false, name, ld_library_path, default_library_path, type,
-                                  permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
-}
-
-bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to,
-                             const char* sonames) {
-  return mock->mock_link_namespaces(false, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
-}
-
-struct android_namespace_t* android_get_exported_namespace(const char* name) {
-  return TO_ANDROID_NAMESPACE(mock->mock_get_exported_namespace(false, name));
-}
-
-void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info) {
-  return mock->mock_dlopen_ext(false, filename, flags, TO_MOCK_NAMESPACE(info->library_namespace));
-}
-
-// libnativebridge APIs
-bool NativeBridgeIsSupported(const char* libpath) {
-  return mock->NativeBridgeIsSupported(libpath);
-}
-
-struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
-  return TO_BRIDGED_NAMESPACE(mock->mock_get_exported_namespace(true, name));
-}
-
-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) {
-  return TO_BRIDGED_NAMESPACE(
-      mock->mock_create_namespace(true, name, ld_library_path, default_library_path, type,
-                                  permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
-}
-
-bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
-                                struct native_bridge_namespace_t* to, const char* sonames) {
-  return mock->mock_link_namespaces(true, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
-}
-
-void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
-                                 struct native_bridge_namespace_t* ns) {
-  return mock->mock_dlopen_ext(true, libpath, flag, TO_MOCK_NAMESPACE(ns));
-}
-
-bool NativeBridgeInitialized() {
-  return mock->NativeBridgeInitialized();
-}
-
-bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
-                                        const char* anon_ns_library_path) {
-  return mock->mock_init_anonymous_namespace(true, public_ns_sonames, anon_ns_library_path);
-}
-
-const char* NativeBridgeGetError() {
-  return mock->NativeBridgeGetError();
-}
-
-bool NativeBridgeIsPathSupported(const char* path) {
-  return mock->NativeBridgeIsPathSupported(path);
-}
-
-}  // extern "C"
-
-// A very simple JNI mock.
-// jstring is a pointer to utf8 char array. We don't need utf16 char here.
-// jobject, jclass, and jmethodID are also a pointer to utf8 char array
-// Only a few JNI methods that are actually used in libnativeloader are mocked.
-JNINativeInterface* CreateJNINativeInterface() {
-  JNINativeInterface* inf = new JNINativeInterface();
-  memset(inf, 0, sizeof(JNINativeInterface));
-
-  inf->GetStringUTFChars = [](JNIEnv*, jstring s, jboolean*) -> const char* {
-    return reinterpret_cast<const char*>(s);
-  };
-
-  inf->ReleaseStringUTFChars = [](JNIEnv*, jstring, const char*) -> void { return; };
-
-  inf->NewStringUTF = [](JNIEnv*, const char* bytes) -> jstring {
-    return reinterpret_cast<jstring>(const_cast<char*>(bytes));
-  };
-
-  inf->FindClass = [](JNIEnv*, const char* name) -> jclass {
-    return reinterpret_cast<jclass>(const_cast<char*>(name));
-  };
-
-  inf->CallObjectMethodV = [](JNIEnv*, jobject obj, jmethodID mid, va_list) -> jobject {
-    if (strcmp("getParent", reinterpret_cast<const char*>(mid)) == 0) {
-      // JniObject_getParent can be a valid jobject or nullptr if there is
-      // no parent classloader.
-      const char* ret = mock->JniObject_getParent(reinterpret_cast<const char*>(obj));
-      return reinterpret_cast<jobject>(const_cast<char*>(ret));
-    }
-    return nullptr;
-  };
-
-  inf->GetMethodID = [](JNIEnv*, jclass, const char* name, const char*) -> jmethodID {
-    return reinterpret_cast<jmethodID>(const_cast<char*>(name));
-  };
-
-  inf->NewWeakGlobalRef = [](JNIEnv*, jobject obj) -> jobject { return obj; };
-
-  inf->IsSameObject = [](JNIEnv*, jobject a, jobject b) -> jboolean {
-    return strcmp(reinterpret_cast<const char*>(a), reinterpret_cast<const char*>(b)) == 0;
-  };
-
-  return inf;
-}
-
-static void* const any_nonnull = reinterpret_cast<void*>(0x12345678);
-
-// Custom matcher for comparing namespace handles
-MATCHER_P(NsEq, other, "") {
-  *result_listener << "comparing " << reinterpret_cast<const char*>(arg) << " and " << other;
-  return strcmp(reinterpret_cast<const char*>(arg), reinterpret_cast<const char*>(other)) == 0;
-}
-
-/////////////////////////////////////////////////////////////////
-
-// Test fixture
-class NativeLoaderTest : public ::testing::TestWithParam<bool> {
- protected:
-  bool IsBridged() { return GetParam(); }
-
-  void SetUp() override {
-    mock = std::make_unique<NiceMock<MockPlatform>>(IsBridged());
-
-    env = std::make_unique<JNIEnv>();
-    env->functions = CreateJNINativeInterface();
-  }
-
-  void SetExpectations() {
-    std::vector<std::string> default_public_libs =
-        android::base::Split(preloadable_public_libraries(), ":");
-    for (auto l : default_public_libs) {
-      EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
-          .WillOnce(Return(any_nonnull));
-    }
-  }
-
-  void RunTest() { InitializeNativeLoader(); }
-
-  void TearDown() override {
-    ResetNativeLoader();
-    delete env->functions;
-    mock.reset();
-  }
-
-  std::unique_ptr<JNIEnv> env;
-};
-
-/////////////////////////////////////////////////////////////////
-
-TEST_P(NativeLoaderTest, InitializeLoadsDefaultPublicLibraries) {
-  SetExpectations();
-  RunTest();
-}
-
-INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
-
-/////////////////////////////////////////////////////////////////
-
-class NativeLoaderTest_Create : public NativeLoaderTest {
- protected:
-  // Test inputs (initialized to the default values). Overriding these
-  // must be done before calling SetExpectations() and RunTest().
-  uint32_t target_sdk_version = 29;
-  std::string class_loader = "my_classloader";
-  bool is_shared = false;
-  std::string dex_path = "/data/app/foo/classes.dex";
-  std::string library_path = "/data/app/foo/lib/arm";
-  std::string permitted_path = "/data/app/foo/lib";
-
-  // expected output (.. for the default test inputs)
-  std::string expected_namespace_name = "classloader-namespace";
-  uint64_t expected_namespace_flags =
-      ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
-  std::string expected_library_path = library_path;
-  std::string expected_permitted_path = std::string("/data:/mnt/expand:") + permitted_path;
-  std::string expected_parent_namespace = "platform";
-  bool expected_link_with_platform_ns = true;
-  bool expected_link_with_runtime_ns = true;
-  bool expected_link_with_sphal_ns = !vendor_public_libraries().empty();
-  bool expected_link_with_vndk_ns = false;
-  bool expected_link_with_default_ns = false;
-  bool expected_link_with_neuralnetworks_ns = true;
-  std::string expected_shared_libs_to_platform_ns = default_public_libraries();
-  std::string expected_shared_libs_to_runtime_ns = runtime_public_libraries();
-  std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries();
-  std::string expected_shared_libs_to_vndk_ns = vndksp_libraries();
-  std::string expected_shared_libs_to_default_ns = default_public_libraries();
-  std::string expected_shared_libs_to_neuralnetworks_ns = neuralnetworks_public_libraries();
-
-  void SetExpectations() {
-    NativeLoaderTest::SetExpectations();
-
-    ON_CALL(*mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr));
-
-    EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(AnyNumber());
-    EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(AnyNumber());
-
-    EXPECT_CALL(*mock, mock_create_namespace(
-                           Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
-                           StrEq(expected_library_path), expected_namespace_flags,
-                           StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
-        .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
-    if (expected_link_with_platform_ns) {
-      EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("platform"),
-                                              StrEq(expected_shared_libs_to_platform_ns)))
-          .WillOnce(Return(true));
-    }
-    if (expected_link_with_runtime_ns) {
-      EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("runtime"),
-                                              StrEq(expected_shared_libs_to_runtime_ns)))
-          .WillOnce(Return(true));
-    }
-    if (expected_link_with_sphal_ns) {
-      EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"),
-                                              StrEq(expected_shared_libs_to_sphal_ns)))
-          .WillOnce(Return(true));
-    }
-    if (expected_link_with_vndk_ns) {
-      EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("vndk"),
-                                              StrEq(expected_shared_libs_to_vndk_ns)))
-          .WillOnce(Return(true));
-    }
-    if (expected_link_with_default_ns) {
-      EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("default"),
-                                              StrEq(expected_shared_libs_to_default_ns)))
-          .WillOnce(Return(true));
-    }
-    if (expected_link_with_neuralnetworks_ns) {
-      EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("neuralnetworks"),
-                                              StrEq(expected_shared_libs_to_neuralnetworks_ns)))
-          .WillOnce(Return(true));
-    }
-  }
-
-  void RunTest() {
-    NativeLoaderTest::RunTest();
-
-    jstring err = CreateClassLoaderNamespace(
-        env(), target_sdk_version, env()->NewStringUTF(class_loader.c_str()), is_shared,
-        env()->NewStringUTF(dex_path.c_str()), env()->NewStringUTF(library_path.c_str()),
-        env()->NewStringUTF(permitted_path.c_str()));
-
-    // no error
-    EXPECT_EQ(err, nullptr);
-
-    if (!IsBridged()) {
-      struct android_namespace_t* ns =
-          FindNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
-
-      // The created namespace is for this apk
-      EXPECT_EQ(dex_path.c_str(), reinterpret_cast<const char*>(ns));
-    } else {
-      struct NativeLoaderNamespace* ns =
-          FindNativeLoaderNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
-
-      // The created namespace is for the this apk
-      EXPECT_STREQ(dex_path.c_str(),
-                   reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
-    }
-  }
-
-  JNIEnv* env() { return NativeLoaderTest::env.get(); }
-};
-
-TEST_P(NativeLoaderTest_Create, DownloadedApp) {
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, BundledSystemApp) {
-  dex_path = "/system/app/foo/foo.apk";
-  is_shared = true;
-
-  expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, BundledVendorApp) {
-  dex_path = "/vendor/app/foo/foo.apk";
-  is_shared = true;
-
-  expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
-  dex_path = "/vendor/app/foo/foo.apk";
-  is_shared = false;
-
-  expected_namespace_name = "vendor-classloader-namespace";
-  expected_library_path = expected_library_path + ":/vendor/lib";
-  expected_permitted_path = expected_permitted_path + ":/vendor/lib";
-  expected_shared_libs_to_platform_ns =
-      expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
-  expected_link_with_vndk_ns = true;
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, BundledProductApp_pre30) {
-  dex_path = "/product/app/foo/foo.apk";
-  is_shared = true;
-
-  expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, BundledProductApp_post30) {
-  dex_path = "/product/app/foo/foo.apk";
-  is_shared = true;
-  target_sdk_version = 30;
-
-  expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, UnbundledProductApp_pre30) {
-  dex_path = "/product/app/foo/foo.apk";
-  is_shared = false;
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, UnbundledProductApp_post30) {
-  dex_path = "/product/app/foo/foo.apk";
-  is_shared = false;
-  target_sdk_version = 30;
-
-  expected_namespace_name = "vendor-classloader-namespace";
-  expected_library_path = expected_library_path + ":/product/lib:/system/product/lib";
-  expected_permitted_path = expected_permitted_path + ":/product/lib:/system/product/lib";
-  expected_shared_libs_to_platform_ns =
-      expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
-  expected_link_with_vndk_ns = true;
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, NamespaceForSharedLibIsNotUsedAsAnonymousNamespace) {
-  if (IsBridged()) {
-    // There is no shared lib in translated arch
-    // TODO(jiyong): revisit this
-    return;
-  }
-  // compared to apks, for java shared libs, library_path is empty; java shared
-  // libs don't have their own native libs. They use platform's.
-  library_path = "";
-  expected_library_path = library_path;
-  // no ALSO_USED_AS_ANONYMOUS
-  expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
-  SetExpectations();
-  RunTest();
-}
-
-TEST_P(NativeLoaderTest_Create, TwoApks) {
-  SetExpectations();
-  const uint32_t second_app_target_sdk_version = 29;
-  const std::string second_app_class_loader = "second_app_classloader";
-  const bool second_app_is_shared = false;
-  const std::string second_app_dex_path = "/data/app/bar/classes.dex";
-  const std::string second_app_library_path = "/data/app/bar/lib/arm";
-  const std::string second_app_permitted_path = "/data/app/bar/lib";
-  const std::string expected_second_app_permitted_path =
-      std::string("/data:/mnt/expand:") + second_app_permitted_path;
-  const std::string expected_second_app_parent_namespace = "classloader-namespace";
-  // no ALSO_USED_AS_ANONYMOUS
-  const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
-
-  // The scenario is that second app is loaded by the first app.
-  // So the first app's classloader (`classloader`) is parent of the second
-  // app's classloader.
-  ON_CALL(*mock, JniObject_getParent(StrEq(second_app_class_loader)))
-      .WillByDefault(Return(class_loader.c_str()));
-
-  // namespace for the second app is created. Its parent is set to the namespace
-  // of the first app.
-  EXPECT_CALL(*mock, mock_create_namespace(
-                         Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
-                         StrEq(second_app_library_path), expected_second_namespace_flags,
-                         StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
-      .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
-  EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), NsEq(second_app_dex_path.c_str()), _, _))
-      .WillRepeatedly(Return(true));
-
-  RunTest();
-  jstring err = CreateClassLoaderNamespace(
-      env(), second_app_target_sdk_version, env()->NewStringUTF(second_app_class_loader.c_str()),
-      second_app_is_shared, env()->NewStringUTF(second_app_dex_path.c_str()),
-      env()->NewStringUTF(second_app_library_path.c_str()),
-      env()->NewStringUTF(second_app_permitted_path.c_str()));
-
-  // success
-  EXPECT_EQ(err, nullptr);
-
-  if (!IsBridged()) {
-    struct android_namespace_t* ns =
-        FindNamespaceByClassLoader(env(), env()->NewStringUTF(second_app_class_loader.c_str()));
-
-    // The created namespace is for the second apk
-    EXPECT_EQ(second_app_dex_path.c_str(), reinterpret_cast<const char*>(ns));
-  } else {
-    struct NativeLoaderNamespace* ns = FindNativeLoaderNamespaceByClassLoader(
-        env(), env()->NewStringUTF(second_app_class_loader.c_str()));
-
-    // The created namespace is for the second apk
-    EXPECT_STREQ(second_app_dex_path.c_str(),
-                 reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
-
-const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
-    [](const struct ConfigEntry&) -> Result<bool> { return true; };
-
-TEST(NativeLoaderConfigParser, NamesAndComments) {
-  const char file_content[] = R"(
-######
-
-libA.so
-#libB.so
-
-
-      libC.so
-libD.so
-    #### libE.so
-)";
-  const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"};
-  Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
-  ASSERT_TRUE(result) << result.error().message();
-  ASSERT_EQ(expected_result, *result);
-}
-
-TEST(NativeLoaderConfigParser, WithBitness) {
-  const char file_content[] = R"(
-libA.so 32
-libB.so 64
-libC.so
-)";
-#if defined(__LP64__)
-  const std::vector<std::string> expected_result = {"libB.so", "libC.so"};
-#else
-  const std::vector<std::string> expected_result = {"libA.so", "libC.so"};
-#endif
-  Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
-  ASSERT_TRUE(result) << result.error().message();
-  ASSERT_EQ(expected_result, *result);
-}
-
-TEST(NativeLoaderConfigParser, WithNoPreload) {
-  const char file_content[] = R"(
-libA.so nopreload
-libB.so nopreload
-libC.so
-)";
-
-  const std::vector<std::string> expected_result = {"libC.so"};
-  Result<std::vector<std::string>> result =
-      ParseConfig(file_content,
-                  [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
-  ASSERT_TRUE(result) << result.error().message();
-  ASSERT_EQ(expected_result, *result);
-}
-
-TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) {
-  const char file_content[] = R"(
-libA.so nopreload 32
-libB.so 64 nopreload
-libC.so 32
-libD.so 64
-libE.so nopreload
-)";
-
-#if defined(__LP64__)
-  const std::vector<std::string> expected_result = {"libD.so"};
-#else
-  const std::vector<std::string> expected_result = {"libC.so"};
-#endif
-  Result<std::vector<std::string>> result =
-      ParseConfig(file_content,
-                  [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
-  ASSERT_TRUE(result) << result.error().message();
-  ASSERT_EQ(expected_result, *result);
-}
-
-TEST(NativeLoaderConfigParser, RejectMalformed) {
-  ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true));
-  ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true));
-  ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true));
-  ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true));
-  ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true));
-  ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true));
-}
-
-}  // namespace nativeloader
-}  // namespace android
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
deleted file mode 100644
index 93df1d0..0000000
--- a/libnativeloader/public_libraries.cpp
+++ /dev/null
@@ -1,366 +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 "nativeloader"
-
-#include "public_libraries.h"
-
-#include <dirent.h>
-
-#include <algorithm>
-#include <memory>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/result.h>
-#include <android-base/strings.h>
-#include <log/log.h>
-
-#include "utils.h"
-
-namespace android::nativeloader {
-
-using namespace internal;
-using namespace ::std::string_literals;
-using android::base::ErrnoError;
-using android::base::Errorf;
-using android::base::Result;
-
-namespace {
-
-constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
-constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
-constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
-constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
-constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
-constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
-
-const std::vector<const std::string> kArtApexPublicLibraries = {
-    "libicuuc.so",
-    "libicui18n.so",
-};
-
-constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB;
-
-constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
-
-// TODO(b/130388701): do we need this?
-std::string root_dir() {
-  static const char* android_root_env = getenv("ANDROID_ROOT");
-  return android_root_env != nullptr ? android_root_env : "/system";
-}
-
-bool debuggable() {
-  static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
-  return debuggable;
-}
-
-std::string vndk_version_str() {
-  static std::string version = android::base::GetProperty("ro.vndk.version", "");
-  if (version != "" && version != "current") {
-    return "." + version;
-  }
-  return "";
-}
-
-// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
-// variable to add libraries to the list. This is intended for platform tests only.
-std::string additional_public_libraries() {
-  if (debuggable()) {
-    const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
-    return val ? val : "";
-  }
-  return "";
-}
-
-void InsertVndkVersionStr(std::string* file_name) {
-  CHECK(file_name != nullptr);
-  size_t insert_pos = file_name->find_last_of(".");
-  if (insert_pos == std::string::npos) {
-    insert_pos = file_name->length();
-  }
-  file_name->insert(insert_pos, vndk_version_str());
-}
-
-const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
-    [](const struct ConfigEntry&) -> Result<bool> { return true; };
-
-Result<std::vector<std::string>> ReadConfig(
-    const std::string& configFile,
-    const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
-  std::string file_content;
-  if (!base::ReadFileToString(configFile, &file_content)) {
-    return ErrnoError();
-  }
-  Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
-  if (!result) {
-    return Errorf("Cannot parse {}: {}", configFile, result.error().message());
-  }
-  return result;
-}
-
-void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
-  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
-  if (dir != nullptr) {
-    // Failing to opening the dir is not an error, which can happen in
-    // webview_zygote.
-    while (struct dirent* ent = readdir(dir.get())) {
-      if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
-        continue;
-      }
-      const std::string filename(ent->d_name);
-      std::string_view fn = filename;
-      if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
-          android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
-        const std::string company_name(fn);
-        const std::string config_file_path = dirname + "/"s + filename;
-        LOG_ALWAYS_FATAL_IF(
-            company_name.empty(),
-            "Error extracting company name from public native library list file path \"%s\"",
-            config_file_path.c_str());
-
-        auto ret = ReadConfig(
-            config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> {
-              if (android::base::StartsWith(entry.soname, "lib") &&
-                  android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
-                return true;
-              } else {
-                return Errorf("Library name \"{}\" does not end with the company name {}.",
-                              entry.soname, company_name);
-              }
-            });
-        if (ret) {
-          sonames->insert(sonames->end(), ret->begin(), ret->end());
-        } else {
-          LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
-                           config_file_path.c_str(), ret.error().message().c_str());
-        }
-      }
-    }
-  }
-}
-
-static std::string InitDefaultPublicLibraries(bool for_preload) {
-  std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
-  auto sonames =
-      ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> {
-        if (for_preload) {
-          return !entry.nopreload;
-        } else {
-          return true;
-        }
-      });
-  if (!sonames) {
-    LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
-                     config_file.c_str(), sonames.error().message().c_str());
-    return "";
-  }
-
-  std::string additional_libs = additional_public_libraries();
-  if (!additional_libs.empty()) {
-    auto vec = base::Split(additional_libs, ":");
-    std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames));
-  }
-
-  // Remove the public libs in the runtime namespace.
-  // These libs are listed in public.android.txt, but we don't want the rest of android
-  // in default namespace to dlopen the libs.
-  // For example, libicuuc.so is exposed to classloader namespace from runtime namespace.
-  // Unfortunately, it does not have stable C symbols, and default namespace should only use
-  // stable symbols in libandroidicu.so. http://b/120786417
-  for (const std::string& lib_name : kArtApexPublicLibraries) {
-    std::string path(kArtApexLibPath);
-    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);
-    }
-  }
-
-  // Remove the public libs in the nnapi namespace.
-  auto it = std::find(sonames->begin(), sonames->end(), kNeuralNetworksApexPublicLibrary);
-  if (it != sonames->end()) {
-    sonames->erase(it);
-  }
-  return android::base::Join(*sonames, ':');
-}
-
-static std::string InitArtPublicLibraries() {
-  CHECK(sizeof(kArtApexPublicLibraries) > 0);
-  std::string list = android::base::Join(kArtApexPublicLibraries, ":");
-
-  std::string additional_libs = additional_public_libraries();
-  if (!additional_libs.empty()) {
-    list = list + ':' + additional_libs;
-  }
-  return list;
-}
-
-static std::string InitVendorPublicLibraries() {
-  // This file is optional, quietly ignore if the file does not exist.
-  auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
-  if (!sonames) {
-    return "";
-  }
-  return android::base::Join(*sonames, ':');
-}
-
-// read /system/etc/public.libraries-<companyname>.txt and
-// /product/etc/public.libraries-<companyname>.txt which contain partner defined
-// system libs that are exposed to apps. The libs in the txt files must be
-// named as lib<name>.<companyname>.so.
-static std::string InitExtendedPublicLibraries() {
-  std::vector<std::string> sonames;
-  ReadExtensionLibraries("/system/etc", &sonames);
-  ReadExtensionLibraries("/product/etc", &sonames);
-  return android::base::Join(sonames, ':');
-}
-
-static std::string InitLlndkLibraries() {
-  std::string config_file = kLlndkLibrariesFile;
-  InsertVndkVersionStr(&config_file);
-  auto sonames = ReadConfig(config_file, always_true);
-  if (!sonames) {
-    LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
-    return "";
-  }
-  return android::base::Join(*sonames, ':');
-}
-
-static std::string InitVndkspLibraries() {
-  std::string config_file = kVndkLibrariesFile;
-  InsertVndkVersionStr(&config_file);
-  auto sonames = ReadConfig(config_file, always_true);
-  if (!sonames) {
-    LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
-    return "";
-  }
-  return android::base::Join(*sonames, ':');
-}
-
-static std::string InitNeuralNetworksPublicLibraries() {
-  return kNeuralNetworksApexPublicLibrary;
-}
-
-}  // namespace
-
-const std::string& preloadable_public_libraries() {
-  static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true);
-  return list;
-}
-
-const std::string& default_public_libraries() {
-  static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
-  return list;
-}
-
-const std::string& runtime_public_libraries() {
-  static std::string list = InitArtPublicLibraries();
-  return list;
-}
-
-const std::string& vendor_public_libraries() {
-  static std::string list = InitVendorPublicLibraries();
-  return list;
-}
-
-const std::string& extended_public_libraries() {
-  static std::string list = InitExtendedPublicLibraries();
-  return list;
-}
-
-const std::string& neuralnetworks_public_libraries() {
-  static std::string list = InitNeuralNetworksPublicLibraries();
-  return list;
-}
-
-const std::string& llndk_libraries() {
-  static std::string list = InitLlndkLibraries();
-  return list;
-}
-
-const std::string& vndksp_libraries() {
-  static std::string list = InitVndkspLibraries();
-  return list;
-}
-
-namespace internal {
-// Exported for testing
-Result<std::vector<std::string>> ParseConfig(
-    const std::string& file_content,
-    const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
-  std::vector<std::string> lines = base::Split(file_content, "\n");
-
-  std::vector<std::string> sonames;
-  for (auto& line : lines) {
-    auto trimmed_line = base::Trim(line);
-    if (trimmed_line[0] == '#' || trimmed_line.empty()) {
-      continue;
-    }
-
-    std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
-    if (tokens.size() < 1 || tokens.size() > 3) {
-      return Errorf("Malformed line \"{}\"", line);
-    }
-    struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL};
-    size_t i = tokens.size();
-    while (i-- > 0) {
-      if (tokens[i] == "nopreload") {
-        entry.nopreload = true;
-      } else if (tokens[i] == "32" || tokens[i] == "64") {
-        if (entry.bitness != ALL) {
-          return Errorf("Malformed line \"{}\": bitness can be specified only once", line);
-        }
-        entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64;
-      } else {
-        if (i != 0) {
-          return Errorf("Malformed line \"{}\"", line);
-        }
-        entry.soname = tokens[i];
-      }
-    }
-
-    // skip 32-bit lib on 64-bit process and vice versa
-#if defined(__LP64__)
-    if (entry.bitness == ONLY_32) continue;
-#else
-    if (entry.bitness == ONLY_64) continue;
-#endif
-
-    Result<bool> ret = filter_fn(entry);
-    if (!ret) {
-      return ret.error();
-    }
-    if (*ret) {
-      // filter_fn has returned true.
-      sonames.push_back(entry.soname);
-    }
-  }
-  return sonames;
-}
-
-}  // namespace internal
-
-}  // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
deleted file mode 100644
index 2de4172..0000000
--- a/libnativeloader/public_libraries.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 <algorithm>
-#include <string>
-
-#include <android-base/result.h>
-
-namespace android::nativeloader {
-
-using android::base::Result;
-
-// These provide the list of libraries that are available to the namespace for apps.
-// Not all of the libraries are available to apps. Depending on the context,
-// e.g., if it is a vendor app or not, different set of libraries are made available.
-const std::string& preloadable_public_libraries();
-const std::string& default_public_libraries();
-const std::string& runtime_public_libraries();
-const std::string& vendor_public_libraries();
-const std::string& extended_public_libraries();
-const std::string& neuralnetworks_public_libraries();
-const std::string& llndk_libraries();
-const std::string& vndksp_libraries();
-
-// These are exported for testing
-namespace internal {
-
-enum Bitness { ALL = 0, ONLY_32, ONLY_64 };
-
-struct ConfigEntry {
-  std::string soname;
-  bool nopreload;
-  Bitness bitness;
-};
-
-Result<std::vector<std::string>> ParseConfig(
-    const std::string& file_content,
-    const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn);
-
-}  // namespace internal
-
-}  // namespace android::nativeloader
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
deleted file mode 100644
index 4d5c53d..0000000
--- a/libnativeloader/test/Android.bp
+++ /dev/null
@@ -1,82 +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_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
deleted file mode 100644
index 65e7b09..0000000
--- a/libnativeloader/test/Android.mk
+++ /dev/null
@@ -1,57 +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.
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-oem1.txt
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-oem2.txt
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-product1.txt
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := oemlibrarytest-system
-LOCAL_MODULE_TAGS := tests
-LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_MODULE_PATH := $(TARGET_OUT_APPS)
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := oemlibrarytest-vendor
-LOCAL_MODULE_TAGS := tests
-LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_APPS)
-include $(BUILD_PACKAGE)
diff --git a/libnativeloader/test/api_test.c b/libnativeloader/test/api_test.c
deleted file mode 100644
index e7025fd..0000000
--- a/libnativeloader/test/api_test.c
+++ /dev/null
@@ -1,25 +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.
- */
-
-/* The main purpose of this test is to ensure this C header compiles in C, so
- * that no C++ features inadvertently leak into the C ABI. */
-#include "nativeloader/native_loader.h"
-
-int main(int argc, char** argv) {
-  (void)argc;
-  (void)argv;
-  return 0;
-}
diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt
deleted file mode 100644
index f9433e2..0000000
--- a/libnativeloader/test/public.libraries-oem1.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.oem1.so
-libbar.oem1.so
diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt
deleted file mode 100644
index de6bdb0..0000000
--- a/libnativeloader/test/public.libraries-oem2.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.oem2.so
-libbar.oem2.so
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
deleted file mode 100644
index 358154c..0000000
--- a/libnativeloader/test/public.libraries-product1.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.product1.so
-libbar.product1.so
diff --git a/libnativeloader/test/runtest.sh b/libnativeloader/test/runtest.sh
deleted file mode 100755
index 40beb5b..0000000
--- a/libnativeloader/test/runtest.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/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
deleted file mode 100644
index a7a455d..0000000
--- a/libnativeloader/test/src/android/test/app/TestActivity.java
+++ /dev/null
@@ -1,44 +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 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
deleted file mode 100644
index c304889..0000000
--- a/libnativeloader/test/system/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.test.app.system">
-
-    <application>
-        <activity android:name="android.test.app.TestActivity" >
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
-
diff --git a/libnativeloader/test/test.cpp b/libnativeloader/test/test.cpp
deleted file mode 100644
index b166928..0000000
--- a/libnativeloader/test/test.cpp
+++ /dev/null
@@ -1,21 +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 "oemlib"
-#include <android-base/logging.h>
-
-static __attribute__((constructor)) void test_lib_init() {
-  LOG(DEBUG) << LIBNAME << " loaded";
-}
diff --git a/libnativeloader/test/vendor/AndroidManifest.xml b/libnativeloader/test/vendor/AndroidManifest.xml
deleted file mode 100644
index c4c1a9c..0000000
--- a/libnativeloader/test/vendor/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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 a098d59..6af49bb 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -362,10 +362,12 @@
     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 0740e7d..b56dcdb 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -19,4 +19,5 @@
         "libpackagelistparser",
     ],
     test_suites: ["device-tests"],
+    require_root: true,
 }
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
index bfa730a..27b9a03 100644
--- a/libprocessgroup/OWNERS
+++ b/libprocessgroup/OWNERS
@@ -1,2 +1,3 @@
 ccross@google.com
+surenb@google.com
 tomcherry@google.com
diff --git a/libprocessgroup/include/processgroup/sched_policy.h b/libprocessgroup/include/processgroup/sched_policy.h
index 3c498da..945d90c 100644
--- a/libprocessgroup/include/processgroup/sched_policy.h
+++ b/libprocessgroup/include/processgroup/sched_policy.h
@@ -70,11 +70,22 @@
 extern int get_sched_policy(int tid, SchedPolicy* policy);
 
 /* Return a displayable string corresponding to policy.
- * Return value: non-NULL NUL-terminated name of unspecified length;
+ * Return value: NUL-terminated name of unspecified length, nullptr if invalid;
  * 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 7c191be..7b6dde2 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -117,43 +117,11 @@
 
 bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
                         bool use_fd_cache) {
-    const TaskProfiles& tp = TaskProfiles::GetInstance();
-
-    for (const auto& name : profiles) {
-        TaskProfile* profile = tp.GetProfile(name);
-        if (profile != nullptr) {
-            if (use_fd_cache) {
-                profile->EnableResourceCaching();
-            }
-            if (!profile->ExecuteForProcess(uid, pid)) {
-                PLOG(WARNING) << "Failed to apply " << name << " process profile";
-            }
-        } else {
-            PLOG(WARNING) << "Failed to find " << name << "process profile";
-        }
-    }
-
-    return true;
+    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, use_fd_cache);
 }
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
-    const TaskProfiles& tp = TaskProfiles::GetInstance();
-
-    for (const auto& name : profiles) {
-        TaskProfile* profile = tp.GetProfile(name);
-        if (profile != nullptr) {
-            if (use_fd_cache) {
-                profile->EnableResourceCaching();
-            }
-            if (!profile->ExecuteForTask(tid)) {
-                PLOG(WARNING) << "Failed to apply " << name << " task profile";
-            }
-        } else {
-            PLOG(WARNING) << "Failed to find " << name << "task profile";
-        }
-    }
-
-    return true;
+    return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
 }
 
 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 74a39cd..608f007 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -15,7 +15,6 @@
       "Controller": "cpuset",
       "File": "top-app/cpus"
     },
-
     {
       "Name": "MemLimit",
       "Controller": "memory",
@@ -494,5 +493,52 @@
         }
       ]
     }
+  ],
+
+  "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 578f0d3..1de4395 100644
--- a/libprocessgroup/profiles/task_profiles.proto
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -18,10 +18,11 @@
 
 package android.profiles;
 
-// Next: 3
+// Next: 4
 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
@@ -42,3 +43,9 @@
     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/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index c83df1a..16339d3 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -46,34 +46,17 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid,
-                                   {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
-                                    "TimerSlackHigh"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_BACKGROUND"}, true) ? 0 : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid,
-                                   {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
-                                    "TimerSlackNormal"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND"}, true) ? 0 : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid,
-                                   {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
-                                    "TimerSlackNormal"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_TOP_APP"}, true) ? 0 : -1;
         case SP_SYSTEM:
-            return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_SYSTEM"}, true) ? 0 : -1;
         case SP_RESTRICTED:
-            return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}, true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"CPUSET_SP_RESTRICTED"}, true) ? 0 : -1;
         default:
             break;
     }
@@ -134,29 +117,17 @@
 
     switch (policy) {
         case SP_BACKGROUND:
-            return SetTaskProfiles(tid, {"HighEnergySaving", "LowIoPriority", "TimerSlackHigh"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
         case SP_FOREGROUND:
         case SP_AUDIO_APP:
         case SP_AUDIO_SYS:
-            return SetTaskProfiles(tid, {"HighPerformance", "HighIoPriority", "TimerSlackNormal"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
         case SP_TOP_APP:
-            return SetTaskProfiles(tid, {"MaxPerformance", "MaxIoPriority", "TimerSlackNormal"},
-                                   true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
         case SP_RT_APP:
-            return SetTaskProfiles(
-                           tid, {"RealtimePerformance", "MaxIoPriority", "TimerSlackNormal"}, true)
-                           ? 0
-                           : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
         default:
-            return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
+            return SetTaskProfiles(tid, {"SCHED_SP_DEFAULT"}, true) ? 0 : -1;
     }
 
     return 0;
@@ -241,7 +212,45 @@
     };
     static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
     if (policy < SP_BACKGROUND || policy >= SP_CNT) {
-        return "error";
+        return nullptr;
     }
     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 aee5f0c..9447f86 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -268,6 +268,26 @@
     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)) {
@@ -373,15 +393,13 @@
         }
     }
 
-    std::map<std::string, std::string> params;
-
     const Json::Value& profiles_val = root["Profiles"];
     for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
         const Json::Value& profile_val = profiles_val[i];
 
         std::string profile_name = profile_val["Name"].asString();
         const Json::Value& actions = profile_val["Actions"];
-        auto profile = std::make_unique<TaskProfile>();
+        auto profile = std::make_shared<TaskProfile>();
 
         for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
             const Json::Value& action_val = actions[act_idx];
@@ -440,7 +458,38 @@
                 LOG(WARNING) << "Unknown profile action: " << action_name;
             }
         }
-        profiles_[profile_name] = std::move(profile);
+        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;
+        }
     }
 
     return true;
@@ -463,3 +512,39 @@
     }
     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 891d5b5..9f2308c 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -154,6 +154,19 @@
     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
@@ -162,9 +175,12 @@
     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::unique_ptr<TaskProfile>> profiles_;
+    std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
     std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
 
     TaskProfiles();
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 2ec4754..88146e9 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -82,3 +82,15 @@
         },
     },
 }
+
+cc_fuzz {
+    name: "sparse_fuzzer",
+    host_supported: false,
+    srcs: [
+        "sparse_fuzzer.cpp",
+    ],
+    static_libs: [
+        "libsparse",
+        "liblog",
+    ],
+}
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
new file mode 100644
index 0000000..42f331f
--- /dev/null
+++ b/libsparse/sparse_fuzzer.cpp
@@ -0,0 +1,16 @@
+#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/statsd_writer.c b/libstats/statsd_writer.c
index b1c05ea..073b67f 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -36,25 +36,6 @@
 #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;
@@ -221,14 +202,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 = htole32(atomic_load(&log_error));
+            buffer.header.tag = atomic_load(&log_error);
             buffer.payload.type = EVENT_TYPE_LONG;
             // format:
             // |atom_tag|dropped_count|
             int64_t composed_long = atomic_load(&atom_tag);
             // Send 2 int32's via an int64.
             composed_long = ((composed_long << 32) | ((int64_t)snapshot));
-            buffer.payload.data = htole64(composed_long);
+            buffer.payload.data = composed_long;
 
             newVec[headerLength].iov_base = &buffer;
             newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index da18af6..2573b1c 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -183,6 +183,7 @@
         "tests/ElfTestUtils.cpp",
         "tests/IsolatedSettings.cpp",
         "tests/JitDebugTest.cpp",
+        "tests/LocalUpdatableMapsTest.cpp",
         "tests/LogFake.cpp",
         "tests/MapInfoCreateMemoryTest.cpp",
         "tests/MapInfoGetBuildIDTest.cpp",
@@ -238,12 +239,14 @@
         "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/*",
diff --git a/libunwindstack/AndroidVersions.md b/libunwindstack/AndroidVersions.md
new file mode 100644
index 0000000..234f639
--- /dev/null
+++ b/libunwindstack/AndroidVersions.md
@@ -0,0 +1,116 @@
+# 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 eaf867f..dff7a8b 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -22,6 +22,9 @@
 
 #include <memory>
 
+#define LOG_TAG "unwind"
+#include <log/log.h>
+
 #include <android-base/unique_fd.h>
 #include <art_api/dex_file_support.h>
 
@@ -32,6 +35,19 @@
 
 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()) {
@@ -57,6 +73,10 @@
 
 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;
@@ -75,6 +95,10 @@
 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/DwarfEhFrameWithHdr.cpp b/libunwindstack/DwarfEhFrameWithHdr.cpp
index 802beca..24b94f0 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.cpp
+++ b/libunwindstack/DwarfEhFrameWithHdr.cpp
@@ -32,8 +32,8 @@
 }
 
 template <typename AddressType>
-bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
-  load_bias_ = load_bias;
+bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, int64_t section_bias) {
+  section_bias_ = section_bias;
 
   memory_.clear_func_offset();
   memory_.clear_text_offset();
@@ -138,7 +138,7 @@
 
   // Relative encodings require adding in the load bias.
   if (IsEncodingRelative(table_encoding_)) {
-    value += load_bias_;
+    value += section_bias_;
   }
   info->pc = value;
   return info;
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index 0e5eef7..b8dd3dd 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -38,7 +38,7 @@
   using DwarfSectionImpl<AddressType>::entries_offset_;
   using DwarfSectionImpl<AddressType>::entries_end_;
   using DwarfSectionImpl<AddressType>::last_error_;
-  using DwarfSectionImpl<AddressType>::load_bias_;
+  using DwarfSectionImpl<AddressType>::section_bias_;
 
   struct FdeInfo {
     AddressType pc;
@@ -61,7 +61,7 @@
     return pc + this->memory_.cur_offset() - 4;
   }
 
-  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+  bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
 
   const DwarfFde* GetFdeFromPc(uint64_t pc) override;
 
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
index b505900..2e388c6 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_ == static_cast<uint64_t>(-1)) {
+      if (pc_offset_ == INT64_MAX) {
         // Unsupported encoding.
         return false;
       }
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 849a31a..cdb6141 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -333,7 +333,7 @@
   memory_.set_cur_offset(cur_offset);
 
   // The load bias only applies to the start.
-  memory_.set_pc_offset(load_bias_);
+  memory_.set_pc_offset(section_bias_);
   bool valid = memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding, &fde->pc_start);
   fde->pc_start = AdjustPcFromFde(fde->pc_start);
 
@@ -591,8 +591,9 @@
 }
 
 template <typename AddressType>
-bool DwarfSectionImplNoHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
-  load_bias_ = load_bias;
+bool DwarfSectionImplNoHdr<AddressType>::Init(uint64_t offset, uint64_t size,
+                                              int64_t section_bias) {
+  section_bias_ = section_bias;
   entries_offset_ = offset;
   next_entries_offset_ = offset;
   entries_end_ = offset + size;
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 3454913..c141b2e 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -53,7 +53,7 @@
 
   valid_ = interface_->Init(&load_bias_);
   if (valid_) {
-    interface_->InitHeaders(load_bias_);
+    interface_->InitHeaders();
     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.
-  uint64_t load_bias;
+  int64_t load_bias;
   if (gnu->Init(&load_bias)) {
-    gnu->InitHeaders(load_bias);
+    gnu->InitHeaders();
     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 (*memory_address < load_bias_) {
+  if (load_bias_ > 0 && *memory_address < static_cast<uint64_t>(load_bias_)) {
     return false;
   }
 
@@ -229,7 +229,7 @@
 }
 
 bool Elf::IsValidPc(uint64_t pc) {
-  if (!valid_ || pc < load_bias_) {
+  if (!valid_ || (load_bias_ > 0 && pc < static_cast<uint64_t>(load_bias_))) {
     return false;
   }
 
@@ -299,7 +299,7 @@
   return interface.release();
 }
 
-uint64_t Elf::GetLoadBias(Memory* memory) {
+int64_t Elf::GetLoadBias(Memory* memory) {
   if (!IsValidElf(memory)) {
     return 0;
   }
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index bdfee01..e863f22 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -124,10 +124,10 @@
 }
 
 template <typename AddressType>
-void ElfInterface::InitHeadersWithTemplate(uint64_t load_bias) {
+void ElfInterface::InitHeadersWithTemplate() {
   if (eh_frame_hdr_offset_ != 0) {
     eh_frame_.reset(new DwarfEhFrameWithHdr<AddressType>(memory_));
-    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, load_bias)) {
+    if (!eh_frame_->Init(eh_frame_hdr_offset_, eh_frame_hdr_size_, eh_frame_hdr_section_bias_)) {
       eh_frame_.reset(nullptr);
     }
   }
@@ -136,21 +136,23 @@
     // If there is an eh_frame section without an eh_frame_hdr section,
     // or using the frame hdr object failed to init.
     eh_frame_.reset(new DwarfEhFrame<AddressType>(memory_));
-    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, load_bias)) {
+    if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_, eh_frame_section_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_, load_bias)) {
+    if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_, debug_frame_section_bias_)) {
       debug_frame_.reset(nullptr);
       debug_frame_offset_ = 0;
       debug_frame_size_ = static_cast<uint64_t>(-1);
@@ -159,7 +161,7 @@
 }
 
 template <typename EhdrType, typename PhdrType, typename ShdrType>
-bool ElfInterface::ReadAllHeaders(uint64_t* load_bias) {
+bool ElfInterface::ReadAllHeaders(int64_t* load_bias) {
   EhdrType ehdr;
   if (!memory_->ReadFully(0, &ehdr, sizeof(ehdr))) {
     last_error_.code = ERROR_MEMORY_INVALID;
@@ -175,7 +177,7 @@
 }
 
 template <typename EhdrType, typename PhdrType>
-uint64_t ElfInterface::GetLoadBias(Memory* memory) {
+int64_t ElfInterface::GetLoadBias(Memory* memory) {
   EhdrType ehdr;
   if (!memory->ReadFully(0, &ehdr, sizeof(ehdr))) {
     return false;
@@ -187,15 +189,17 @@
     if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
       return 0;
     }
-    if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
-      return phdr.p_vaddr;
+
+    // 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;
     }
   }
   return 0;
 }
 
 template <typename EhdrType, typename PhdrType>
-void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
+void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, int64_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) {
@@ -214,8 +218,8 @@
       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 && phdr.p_vaddr > phdr.p_offset) {
-        *load_bias = phdr.p_vaddr - phdr.p_offset;
+      if (first_exec_load_header) {
+        *load_bias = static_cast<uint64_t>(phdr.p_vaddr) - phdr.p_offset;
       }
       first_exec_load_header = false;
       break;
@@ -224,6 +228,7 @@
     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;
 
@@ -338,24 +343,21 @@
       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") {
-            offset_ptr = &debug_frame_offset_;
-            size_ptr = &debug_frame_size_;
+            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;
           } else if (name == ".gnu_debugdata") {
-            offset_ptr = &gnu_debugdata_offset_;
-            size_ptr = &gnu_debugdata_size_;
+            gnu_debugdata_offset_ = shdr.sh_offset;
+            gnu_debugdata_size_ = shdr.sh_size;
           } else if (name == ".eh_frame") {
-            offset_ptr = &eh_frame_offset_;
-            size_ptr = &eh_frame_size_;
+            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;
           } else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
-            offset_ptr = &eh_frame_hdr_offset_;
-            size_ptr = &eh_frame_hdr_size_;
-          }
-          if (offset_ptr != nullptr) {
-            *offset_ptr = shdr.sh_offset;
-            *size_ptr = shdr.sh_size;
+            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;
           }
         }
       }
@@ -637,16 +639,14 @@
 }
 
 // Instantiate all of the needed template functions.
-template void ElfInterface::InitHeadersWithTemplate<uint32_t>(uint64_t);
-template void ElfInterface::InitHeadersWithTemplate<uint64_t>(uint64_t);
+template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
+template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
 
-template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(uint64_t*);
-template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(uint64_t*);
+template bool ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(int64_t*);
+template bool ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(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::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::ReadSectionHeaders<Elf32_Ehdr, Elf32_Shdr>(const Elf32_Ehdr&);
 template void ElfInterface::ReadSectionHeaders<Elf64_Ehdr, Elf64_Shdr>(const Elf64_Ehdr&);
@@ -668,8 +668,8 @@
 template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
 template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
 
-template uint64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
-template uint64_t ElfInterface::GetLoadBias<Elf64_Ehdr, Elf64_Phdr>(Memory*);
+template int64_t ElfInterface::GetLoadBias<Elf32_Ehdr, Elf32_Phdr>(Memory*);
+template int64_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 3dd5d54..76f2dc8 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -26,7 +26,7 @@
 
 namespace unwindstack {
 
-bool ElfInterfaceArm::Init(uint64_t* load_bias) {
+bool ElfInterfaceArm::Init(int64_t* load_bias) {
   if (!ElfInterface32::Init(load_bias)) {
     return false;
   }
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 4c3a0c3..1d71cac 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(uint64_t* load_bias) override;
+  bool Init(int64_t* section_bias) override;
 
   bool GetPrel31Addr(uint32_t offset, uint32_t* addr);
 
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 5b30a4d..f2dad84 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <stdint.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -263,8 +264,8 @@
 }
 
 uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
-  uint64_t cur_load_bias = load_bias.load();
-  if (cur_load_bias != static_cast<uint64_t>(-1)) {
+  int64_t cur_load_bias = load_bias.load();
+  if (cur_load_bias != INT64_MAX) {
     return cur_load_bias;
   }
 
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 5da73e4..250e600 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -149,9 +149,10 @@
       }
 
       // Never delete these maps, they may be in use. The assumption is
-      // that there will only every be a handfull of these so waiting
+      // that there will only every be a handful 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/include/unwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
index 8dd8d2b..c45699a 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(uint64_t offset) { pc_offset_ = offset; }
-  void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
+  void set_pc_offset(int64_t offset) { pc_offset_ = offset; }
+  void clear_pc_offset() { pc_offset_ = INT64_MAX; }
 
   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;
 
-  uint64_t pc_offset_ = static_cast<uint64_t>(-1);
+  int64_t pc_offset_ = INT64_MAX;
   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 e9942de..0b3f6d4 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, uint64_t load_bias) = 0;
+  virtual bool Init(uint64_t offset, uint64_t size, int64_t section_bias) = 0;
 
   virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
 
@@ -150,7 +150,7 @@
   bool EvalExpression(const DwarfLocation& loc, Memory* regular_memory, AddressType* value,
                       RegsInfo<AddressType>* regs_info, bool* is_dex_pc);
 
-  uint64_t load_bias_ = 0;
+  int64_t section_bias_ = 0;
   uint64_t entries_offset_ = 0;
   uint64_t entries_end_ = 0;
   uint64_t pc_offset_ = 0;
@@ -166,7 +166,7 @@
   using DwarfSectionImpl<AddressType>::entries_offset_;
   using DwarfSectionImpl<AddressType>::entries_end_;
   using DwarfSectionImpl<AddressType>::last_error_;
-  using DwarfSectionImpl<AddressType>::load_bias_;
+  using DwarfSectionImpl<AddressType>::section_bias_;
   using DwarfSectionImpl<AddressType>::cie_entries_;
   using DwarfSectionImpl<AddressType>::fde_entries_;
   using DwarfSectionImpl<AddressType>::cie32_value_;
@@ -175,7 +175,7 @@
   DwarfSectionImplNoHdr(Memory* memory) : DwarfSectionImpl<AddressType>(memory) {}
   virtual ~DwarfSectionImplNoHdr() = default;
 
-  bool Init(uint64_t offset, uint64_t size, uint64_t load_bias) override;
+  bool Init(uint64_t offset, uint64_t size, int64_t section_bias) override;
 
   const DwarfFde* GetFdeFromPc(uint64_t pc) override;
 
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 56bf318..fc3f2a6 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -75,7 +75,7 @@
 
   std::string GetBuildID();
 
-  uint64_t GetLoadBias() { return load_bias_; }
+  int64_t GetLoadBias() { return load_bias_; }
 
   bool IsValidPc(uint64_t pc);
 
@@ -101,7 +101,7 @@
 
   static bool GetInfo(Memory* memory, uint64_t* size);
 
-  static uint64_t GetLoadBias(Memory* memory);
+  static int64_t GetLoadBias(Memory* memory);
 
   static std::string GetBuildID(Memory* memory);
 
@@ -116,7 +116,7 @@
 
  protected:
   bool valid_ = false;
-  uint64_t load_bias_ = 0;
+  int64_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 dbd917d..ae9bd9a 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(uint64_t* load_bias) = 0;
+  virtual bool Init(int64_t* load_bias) = 0;
 
-  virtual void InitHeaders(uint64_t load_bias) = 0;
+  virtual void InitHeaders() = 0;
 
   virtual std::string GetSoname() = 0;
 
@@ -80,10 +80,13 @@
   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_; }
@@ -98,20 +101,20 @@
   uint64_t LastErrorAddress() { return last_error_.address; }
 
   template <typename EhdrType, typename PhdrType>
-  static uint64_t GetLoadBias(Memory* memory);
+  static int64_t GetLoadBias(Memory* memory);
 
   template <typename EhdrType, typename ShdrType, typename NhdrType>
   static std::string ReadBuildIDFromMemory(Memory* memory);
 
  protected:
   template <typename AddressType>
-  void InitHeadersWithTemplate(uint64_t load_bias);
+  void InitHeadersWithTemplate();
 
   template <typename EhdrType, typename PhdrType, typename ShdrType>
-  bool ReadAllHeaders(uint64_t* load_bias);
+  bool ReadAllHeaders(int64_t* load_bias);
 
   template <typename EhdrType, typename PhdrType>
-  void ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias);
+  void ReadProgramHeaders(const EhdrType& ehdr, int64_t* load_bias);
 
   template <typename EhdrType, typename ShdrType>
   void ReadSectionHeaders(const EhdrType& ehdr);
@@ -142,12 +145,15 @@
   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;
@@ -175,13 +181,11 @@
   ElfInterface32(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterface32() = default;
 
-  bool Init(uint64_t* load_bias) override {
+  bool Init(int64_t* load_bias) override {
     return ElfInterface::ReadAllHeaders<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(load_bias);
   }
 
-  void InitHeaders(uint64_t load_bias) override {
-    ElfInterface::InitHeadersWithTemplate<uint32_t>(load_bias);
-  }
+  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint32_t>(); }
 
   std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf32_Dyn>(); }
 
@@ -205,13 +209,11 @@
   ElfInterface64(Memory* memory) : ElfInterface(memory) {}
   virtual ~ElfInterface64() = default;
 
-  bool Init(uint64_t* load_bias) override {
+  bool Init(int64_t* load_bias) override {
     return ElfInterface::ReadAllHeaders<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(load_bias);
   }
 
-  void InitHeaders(uint64_t load_bias) override {
-    ElfInterface::InitHeadersWithTemplate<uint64_t>(load_bias);
-  }
+  void InitHeaders() override { ElfInterface::InitHeadersWithTemplate<uint64_t>(); }
 
   std::string GetSoname() override { return ElfInterface::GetSonameWithTemplate<Elf64_Dyn>(); }
 
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 13ce10f..8f0c516 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -39,7 +39,7 @@
         flags(flags),
         name(name),
         prev_map(map_info),
-        load_bias(static_cast<uint64_t>(-1)),
+        load_bias(INT64_MAX),
         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 +49,7 @@
         flags(flags),
         name(name),
         prev_map(map_info),
-        load_bias(static_cast<uint64_t>(-1)),
+        load_bias(INT64_MAX),
         build_id(0) {}
   ~MapInfo();
 
@@ -59,17 +59,20 @@
   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 from 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 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
   // shared libraries into a read-only and read-execute map.
   uint64_t elf_start_offset = 0;
 
   MapInfo* prev_map = nullptr;
 
-  std::atomic_uint64_t load_bias;
+  std::atomic_int64_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 1784394..e53f367 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -105,7 +105,7 @@
 
   const std::string GetMapsFile() const override;
 
- private:
+ protected:
   std::vector<std::unique_ptr<MapInfo>> saved_maps_;
 };
 
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index b386ef4..a9d6dad 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, uint64_t) override { return false; }
+  bool Init(uint64_t, uint64_t, int64_t) override { return false; }
 
   void GetFdes(std::vector<const DwarfFde*>*) override {}
 
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index d754fcc..953dc75 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -30,23 +30,24 @@
   MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
   virtual ~MockDwarfSection() = default;
 
-  MOCK_METHOD3(Init, bool(uint64_t, uint64_t, uint64_t));
+  MOCK_METHOD(bool, Init, (uint64_t, uint64_t, int64_t), (override));
 
-  MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
+  MOCK_METHOD(bool, Eval, (const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*),
+              (override));
 
-  MOCK_METHOD3(Log, bool(uint8_t, uint64_t, const DwarfFde*));
+  MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*), (override));
 
-  MOCK_METHOD1(GetFdes, void(std::vector<const DwarfFde*>*));
+  MOCK_METHOD(void, GetFdes, (std::vector<const DwarfFde*>*), (override));
 
-  MOCK_METHOD1(GetFdeFromPc, const DwarfFde*(uint64_t));
+  MOCK_METHOD(const DwarfFde*, GetFdeFromPc, (uint64_t), (override));
 
-  MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
+  MOCK_METHOD(bool, GetCfaLocationInfo, (uint64_t, const DwarfFde*, dwarf_loc_regs_t*), (override));
 
-  MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
+  MOCK_METHOD(uint64_t, GetCieOffsetFromFde32, (uint32_t), (override));
 
-  MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
+  MOCK_METHOD(uint64_t, GetCieOffsetFromFde64, (uint64_t), (override));
 
-  MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
+  MOCK_METHOD(uint64_t, AdjustPcFromFde, (uint64_t), (override));
 };
 
 class DwarfSectionTest : public ::testing::Test {
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index bd3083c..832e64a 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(uint64_t*) override { return false; }
-  void InitHeaders(uint64_t) override {}
+  bool Init(int64_t*) override { return false; }
+  void InitHeaders() 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 f9ee9eb..ea27e3e 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -112,6 +112,21 @@
   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);
@@ -131,6 +146,12 @@
   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_;
 };
 
@@ -166,9 +187,9 @@
   phdr.p_align = 0x1000;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000U, load_bias);
+  EXPECT_EQ(0x2000, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -178,11 +199,11 @@
   ASSERT_EQ(0x10000U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, elf32_single_pt_load) {
+TEST_F(ElfInterfaceTest, single_pt_load_32) {
   SinglePtLoad<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_single_pt_load) {
+TEST_F(ElfInterfaceTest, single_pt_load_64) {
   SinglePtLoad<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
 
@@ -222,9 +243,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000U, load_bias);
+  EXPECT_EQ(0x2000, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(3U, pt_loads.size());
@@ -245,11 +266,11 @@
   ASSERT_EQ(0x10002U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, elf32_multiple_executable_pt_loads) {
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_32) {
   MultipleExecutablePtLoads<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_multiple_executable_pt_loads) {
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_64) {
   MultipleExecutablePtLoads<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
 
@@ -289,9 +310,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * (sizeof(phdr) + 100), &phdr, sizeof(phdr));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000U, load_bias);
+  EXPECT_EQ(0x2000, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(3U, pt_loads.size());
@@ -312,12 +333,12 @@
   ASSERT_EQ(0x10002U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, elf32_multiple_executable_pt_loads_increments_not_size_of_phdr) {
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_increments_not_size_of_phdr_32) {
   MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn,
                                                    ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_multiple_executable_pt_loads_increments_not_size_of_phdr) {
+TEST_F(ElfInterfaceTest, multiple_executable_pt_loads_increments_not_size_of_phdr_64) {
   MultipleExecutablePtLoadsIncrementsNotSizeOfPhdr<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn,
                                                    ElfInterface64>();
 }
@@ -358,9 +379,9 @@
   phdr.p_align = 0x1002;
   memory_.SetMemory(0x100 + 2 * sizeof(phdr), &phdr, sizeof(phdr));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x1001U, load_bias);
+  EXPECT_EQ(0x1001, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -371,11 +392,11 @@
   ASSERT_EQ(0x10001U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, elf32_non_executable_pt_loads) {
+TEST_F(ElfInterfaceTest, non_executable_pt_loads_32) {
   NonExecutablePtLoads<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_non_executable_pt_loads) {
+TEST_F(ElfInterfaceTest, non_executable_pt_loads_64) {
   NonExecutablePtLoads<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
 
@@ -428,11 +449,10 @@
   memset(&phdr, 0, sizeof(phdr));
   phdr.p_type = PT_GNU_EH_FRAME;
   memory_.SetMemory(phdr_offset, &phdr, sizeof(phdr));
-  phdr_offset += sizeof(phdr);
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000U, load_bias);
+  EXPECT_EQ(0x2000, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
@@ -443,15 +463,15 @@
   ASSERT_EQ(0x10000U, load_data.table_size);
 }
 
-TEST_F(ElfInterfaceTest, elf32_many_phdrs) {
+TEST_F(ElfInterfaceTest, many_phdrs_32) {
   ElfInterfaceTest::ManyPhdrs<Elf32_Ehdr, Elf32_Phdr, Elf32_Dyn, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_many_phdrs) {
+TEST_F(ElfInterfaceTest, many_phdrs_64) {
   ElfInterfaceTest::ManyPhdrs<Elf64_Ehdr, Elf64_Phdr, Elf64_Dyn, ElfInterface64>();
 }
 
-TEST_F(ElfInterfaceTest, elf32_arm) {
+TEST_F(ElfInterfaceTest, arm32) {
   ElfInterfaceArm elf_arm(&memory_);
 
   Elf32_Ehdr ehdr = {};
@@ -470,9 +490,9 @@
   memory_.SetData32(0x2000, 0x1000);
   memory_.SetData32(0x2008, 0x1000);
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf_arm.Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
 
   std::vector<uint32_t> entries;
   for (auto addr : elf_arm) {
@@ -551,19 +571,19 @@
 void ElfInterfaceTest::Soname() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
 
   ASSERT_EQ("fake_soname.so", elf->GetSoname());
 }
 
-TEST_F(ElfInterfaceTest, elf32_soname) {
+TEST_F(ElfInterfaceTest, soname_32) {
   SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>();
   Soname<ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_soname) {
+TEST_F(ElfInterfaceTest, soname_64) {
   SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>();
   Soname<ElfInterface64>();
 }
@@ -572,19 +592,19 @@
 void ElfInterfaceTest::SonameAfterDtNull() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
 
   ASSERT_EQ("", elf->GetSoname());
 }
 
-TEST_F(ElfInterfaceTest, elf32_soname_after_dt_null) {
+TEST_F(ElfInterfaceTest, soname_after_dt_null_32) {
   SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTNULL_AFTER);
   SonameAfterDtNull<ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_soname_after_dt_null) {
+TEST_F(ElfInterfaceTest, soname_after_dt_null_64) {
   SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTNULL_AFTER);
   SonameAfterDtNull<ElfInterface64>();
 }
@@ -593,19 +613,19 @@
 void ElfInterfaceTest::SonameSize() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
 
   ASSERT_EQ("", elf->GetSoname());
 }
 
-TEST_F(ElfInterfaceTest, elf32_soname_size) {
+TEST_F(ElfInterfaceTest, soname_size_32) {
   SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_DTSIZE_SMALL);
   SonameSize<ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_soname_size) {
+TEST_F(ElfInterfaceTest, soname_size_64) {
   SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_DTSIZE_SMALL);
   SonameSize<ElfInterface64>();
 }
@@ -616,19 +636,19 @@
 void ElfInterfaceTest::SonameMissingMap() {
   std::unique_ptr<ElfInterface> elf(new ElfInterfaceType(&memory_));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
 
   ASSERT_EQ("", elf->GetSoname());
 }
 
-TEST_F(ElfInterfaceTest, elf32_soname_missing_map) {
+TEST_F(ElfInterfaceTest, soname_missing_map_32) {
   SonameInit<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Dyn>(SONAME_MISSING_MAP);
   SonameMissingMap<ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, elf64_soname_missing_map) {
+TEST_F(ElfInterfaceTest, soname_missing_map_64) {
   SonameInit<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Dyn>(SONAME_MISSING_MAP);
   SonameMissingMap<ElfInterface64>();
 }
@@ -647,17 +667,17 @@
   memory_.SetData32(0x10004, 0x500);
   memory_.SetData32(0x10008, 250);
 
-  elf.InitHeaders(0);
+  elf.InitHeaders();
 
   EXPECT_FALSE(elf.eh_frame() == nullptr);
   EXPECT_TRUE(elf.debug_frame() == nullptr);
 }
 
-TEST_F(ElfInterfaceTest, init_headers_eh_frame32) {
+TEST_F(ElfInterfaceTest, init_headers_eh_frame_32) {
   InitHeadersEhFrameTest<ElfInterface32Fake>();
 }
 
-TEST_F(ElfInterfaceTest, init_headers_eh_frame64) {
+TEST_F(ElfInterfaceTest, init_headers_eh_frame_64) {
   InitHeadersEhFrameTest<ElfInterface64Fake>();
 }
 
@@ -679,17 +699,17 @@
   memory_.SetData32(0x5108, 0x1500);
   memory_.SetData32(0x510c, 0x200);
 
-  elf.InitHeaders(0);
+  elf.InitHeaders();
 
   EXPECT_TRUE(elf.eh_frame() == nullptr);
   EXPECT_FALSE(elf.debug_frame() == nullptr);
 }
 
-TEST_F(ElfInterfaceTest, init_headers_debug_frame32) {
+TEST_F(ElfInterfaceTest, init_headers_debug_frame_32) {
   InitHeadersDebugFrame<ElfInterface32Fake>();
 }
 
-TEST_F(ElfInterfaceTest, init_headers_debug_frame64) {
+TEST_F(ElfInterfaceTest, init_headers_debug_frame_64) {
   InitHeadersDebugFrame<ElfInterface64Fake>();
 }
 
@@ -703,16 +723,16 @@
   ehdr.e_phentsize = sizeof(Phdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
 }
 
-TEST_F(ElfInterfaceTest, init_program_headers_malformed32) {
+TEST_F(ElfInterfaceTest, init_program_headers_malformed_32) {
   InitProgramHeadersMalformed<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_program_headers_malformed64) {
+TEST_F(ElfInterfaceTest, init_program_headers_malformed_64) {
   InitProgramHeadersMalformed<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>();
 }
 
@@ -726,16 +746,16 @@
   ehdr.e_shentsize = sizeof(Shdr);
   memory_.SetMemory(0, &ehdr, sizeof(ehdr));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_malformed32) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_32) {
   InitSectionHeadersMalformed<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_malformed64) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_64) {
   InitSectionHeadersMalformed<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
 
@@ -790,11 +810,10 @@
   shdr.sh_offset = 0xf000;
   shdr.sh_size = 0x1000;
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
-  offset += ehdr.e_shentsize;
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
   EXPECT_EQ(0U, elf->debug_frame_offset());
   EXPECT_EQ(0U, elf->debug_frame_size());
   EXPECT_EQ(0U, elf->gnu_debugdata_offset());
@@ -805,11 +824,11 @@
   ASSERT_FALSE(elf->GetFunctionName(0x90010, &name, &name_offset));
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata32) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata_32) {
   InitSectionHeadersMalformedSymData<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata64) {
+TEST_F(ElfInterfaceTest, init_section_headers_malformed_symdata_64) {
   InitSectionHeadersMalformedSymData<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
 }
 
@@ -860,14 +879,13 @@
   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");
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
   EXPECT_EQ(0U, elf->debug_frame_offset());
   EXPECT_EQ(0U, elf->debug_frame_size());
   EXPECT_EQ(0U, elf->gnu_debugdata_offset());
@@ -884,19 +902,19 @@
   EXPECT_EQ(32U, name_offset);
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers32) {
+TEST_F(ElfInterfaceTest, init_section_headers_32) {
   InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(sizeof(Elf32_Shdr));
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers64) {
+TEST_F(ElfInterfaceTest, init_section_headers_64) {
   InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(sizeof(Elf64_Shdr));
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size32) {
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size_32) {
   InitSectionHeaders<Elf32_Ehdr, Elf32_Shdr, Elf32_Sym, ElfInterface32>(0x100);
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size64) {
+TEST_F(ElfInterfaceTest, init_section_headers_non_std_entry_size_64) {
   InitSectionHeaders<Elf64_Ehdr, Elf64_Shdr, Elf64_Sym, ElfInterface64>(0x100);
 }
 
@@ -961,7 +979,7 @@
   shdr.sh_type = SHT_PROGBITS;
   shdr.sh_link = 2;
   shdr.sh_name = 0x400;
-  shdr.sh_addr = 0x6000;
+  shdr.sh_addr = 0xa000;
   shdr.sh_offset = 0xa000;
   shdr.sh_entsize = 0x100;
   shdr.sh_size = 0xf00;
@@ -971,10 +989,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"));
@@ -982,29 +1000,352 @@
   memory_.SetMemory(0xf400, ".eh_frame_hdr", sizeof(".eh_frame_hdr"));
   memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, 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_offsets32) {
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_32) {
   InitSectionHeadersOffsets<Elf32_Ehdr, Elf32_Shdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
+TEST_F(ElfInterfaceTest, init_section_headers_offsets_64) {
   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_));
 
@@ -1022,9 +1363,9 @@
   phdr.p_align = 0x1000;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0, load_bias);
   EXPECT_TRUE(elf->IsValidPc(0));
   EXPECT_TRUE(elf->IsValidPc(0x5000));
   EXPECT_TRUE(elf->IsValidPc(0xffff));
@@ -1048,9 +1389,9 @@
   phdr.p_align = 0x1000;
   memory_.SetMemory(0x100, &phdr, sizeof(phdr));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0x2000U, load_bias);
+  EXPECT_EQ(0x2000, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x1000));
   EXPECT_FALSE(elf->IsValidPc(0x1fff));
@@ -1105,10 +1446,10 @@
   memory_.SetData32(0x708, 0x2100);
   memory_.SetData32(0x70c, 0x200);
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders(0);
-  EXPECT_EQ(0U, load_bias);
+  elf->InitHeaders();
+  EXPECT_EQ(0, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x20ff));
   EXPECT_TRUE(elf->IsValidPc(0x2100));
@@ -1162,10 +1503,10 @@
   memory_.SetData32(0x708, 0x20f8);
   memory_.SetData32(0x70c, 0x200);
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  elf->InitHeaders(0);
-  EXPECT_EQ(0U, load_bias);
+  elf->InitHeaders();
+  EXPECT_EQ(0, load_bias);
   EXPECT_FALSE(elf->IsValidPc(0));
   EXPECT_FALSE(elf->IsValidPc(0x27ff));
   EXPECT_TRUE(elf->IsValidPc(0x2800));
@@ -1201,7 +1542,6 @@
   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;
@@ -1218,16 +1558,23 @@
   shdr.sh_offset = 0xf000;
   shdr.sh_size = 0x1000;
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
-  offset += ehdr.e_shentsize;
 
   memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
   memory_.SetMemory(0xb000, note_section, sizeof(note_section));
 
-  uint64_t load_bias = 0;
+  int64_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_));
@@ -1266,7 +1613,6 @@
   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;
@@ -1283,16 +1629,23 @@
   shdr.sh_offset = 0xf000;
   shdr.sh_size = 0x1000;
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
-  offset += ehdr.e_shentsize;
 
   memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
   memory_.SetMemory(0xb000, note_section, sizeof(note_section));
 
-  uint64_t load_bias = 0;
+  int64_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_));
@@ -1320,7 +1673,6 @@
   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;
@@ -1337,16 +1689,23 @@
   shdr.sh_offset = 0xf000;
   shdr.sh_size = 0x1000;
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
-  offset += ehdr.e_shentsize;
 
   memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
   memory_.SetMemory(0xb000, note_section, sizeof(note_section));
 
-  uint64_t load_bias = 0;
+  int64_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_));
@@ -1374,7 +1733,6 @@
   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;
@@ -1391,16 +1749,23 @@
   shdr.sh_offset = 0xf000;
   shdr.sh_size = 0x1000;
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
-  offset += ehdr.e_shentsize;
 
   memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
   memory_.SetMemory(0xb000, note_section, sizeof(note_section));
 
-  uint64_t load_bias = 0;
+  int64_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_));
@@ -1428,7 +1793,6 @@
   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;
@@ -1445,54 +1809,139 @@
   shdr.sh_offset = 0xf000;
   shdr.sh_size = 0x1000;
   memory_.SetMemory(offset, &shdr, sizeof(shdr));
-  offset += ehdr.e_shentsize;
 
   memory_.SetMemory(0xf500, ".note.gnu.build-id", sizeof(".note.gnu.build-id"));
   memory_.SetMemory(0xb000, note_section, sizeof(note_section));
 
-  uint64_t load_bias = 0;
+  int64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
   ASSERT_EQ("", elf->GetBuildID());
 }
 
-TEST_F(ElfInterfaceTest, build_id32) {
-  BuildID<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id64) {
-  BuildID<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_two_notes32) {
-  BuildIDTwoNotes<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_two_notes64) {
-  BuildIDTwoNotes<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name32) {
-  BuildIDSectionTooSmallForName<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_name64) {
-  BuildIDSectionTooSmallForName<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc32) {
-  BuildIDSectionTooSmallForDesc<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_desc64) {
-  BuildIDSectionTooSmallForDesc<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
-}
-
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header32) {
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header_32) {
   BuildIDSectionTooSmallForHeader<Elf32_Ehdr, Elf32_Shdr, Elf32_Nhdr, ElfInterface32>();
 }
 
-TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header64) {
+TEST_F(ElfInterfaceTest, build_id_section_too_small_for_header_64) {
   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 c432d6d..e6728a0 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(uint64_t*) override { return false; }
-  void InitHeaders(uint64_t) override {}
+  bool Init(int64_t*) override { return false; }
+  void InitHeaders() override {}
   std::string GetSoname() override { return ""; }
   bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
   std::string GetBuildID() override { return ""; }
 
-  MOCK_METHOD4(Step, bool(uint64_t, Regs*, Memory*, bool*));
-  MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
-  MOCK_METHOD1(IsValidPc, bool(uint64_t));
+  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));
 
   void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
   void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
new file mode 100644
index 0000000..b816b9a
--- /dev/null
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index f5ac6cb..da3dbf2 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 = static_cast<uint64_t>(-1);
+  map_info_->load_bias = INT64_MAX;
   elf_->FakeSetLoadBias(0x1000);
   EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
 }
@@ -141,6 +141,7 @@
   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 e2cbb98..ef76b1b 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(static_cast<uint64_t>(-1), map_info.load_bias);
+  EXPECT_EQ(INT64_MAX, 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(static_cast<uint64_t>(-1), map_info.load_bias);
+  EXPECT_EQ(INT64_MAX, map_info.load_bias);
   EXPECT_EQ(0UL, map_info.elf_offset);
   EXPECT_TRUE(map_info.elf.get() == nullptr);
 }
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index bded57a..0d58c09 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1534,4 +1534,99 @@
   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/files/offline/eh_frame_bias_x86/libc.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
new file mode 100644
index 0000000..f3eb615
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
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
new file mode 100644
index 0000000..7d52483
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 0000000..821928e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 0000000..b95bfac
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
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
new file mode 100644
index 0000000..aefdb6b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
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
new file mode 100644
index 0000000..c71dcfb
--- /dev/null
+++ b/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
Binary files differ
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
new file mode 100644
index 0000000..7bb7156
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
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
new file mode 100644
index 0000000..00a3896
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
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
new file mode 100644
index 0000000..a2babee
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
@@ -0,0 +1,7 @@
+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
new file mode 100644
index 0000000..3c601e1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
@@ -0,0 +1,33 @@
+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/stack0.data b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
new file mode 100644
index 0000000..1674733
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
Binary files differ
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
new file mode 100644
index 0000000..6d7b48a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
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
new file mode 100644
index 0000000..3a75b8f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
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
new file mode 100644
index 0000000..4940916
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
Binary files differ
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 4f67d67..64b58a8 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -275,6 +275,9 @@
 
     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/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 6fdc0ed..37bdf05 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -11,8 +11,9 @@
 
 #include <utils/threads.h>
 
+// b/141212746 - increased for virtual platforms with higher volatility
 // # of milliseconds to fudge stopwatch measurements
-#define TIMING_TOLERANCE_MS 25
+#define TIMING_TOLERANCE_MS 100
 
 namespace android {
 
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 5c3cf32..e2a8c59 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -346,7 +346,6 @@
     if (isStaticString()) {
         buf = static_cast<SharedBuffer*>(alloc((size() + 1) * sizeof(char16_t)));
         if (buf) {
-            buf->acquire();
             memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t));
         }
     } else {
@@ -365,7 +364,6 @@
         }
         buf = static_cast<SharedBuffer*>(alloc(newSize));
         if (buf) {
-            buf->acquire();
             memcpy(buf->data(), mString, copySize);
         }
     } else {
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index c3641ef..1172ae7 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -22,7 +22,8 @@
 #include <limits.h>
 #include <time.h>
 
-#if defined(__ANDROID__)
+// host linux support requires Linux 2.6.39+
+#if defined(__linux__)
 nsecs_t systemTime(int clock)
 {
     static const clockid_t clocks[] = {
@@ -41,8 +42,7 @@
 nsecs_t systemTime(int /*clock*/)
 {
     // Clock support varies widely across hosts. Mac OS doesn't support
-    // posix clocks, older glibcs don't support CLOCK_BOOTTIME and Windows
-    // is windows.
+    // CLOCK_BOOTTIME, and Windows is windows.
     struct timeval t;
     t.tv_sec = t.tv_usec = 0;
     gettimeofday(&t, nullptr);
diff --git a/libutils/include/utils/ByteOrder.h b/libutils/include/utils/ByteOrder.h
index 44ea13d..9b940e6 100644
--- a/libutils/include/utils/ByteOrder.h
+++ b/libutils/include/utils/ByteOrder.h
@@ -14,10 +14,17 @@
  * limitations under the License.
  */
 
-//
+#pragma once
 
-#ifndef _LIBS_UTILS_BYTE_ORDER_H
-#define _LIBS_UTILS_BYTE_ORDER_H
+/*
+ * 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.
+ */
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -27,55 +34,12 @@
 #include <netinet/in.h>
 #endif
 
-/*
- * 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.
- */
+/* TODO: move this cruft to frameworks/. */
 
-static inline uint32_t android_swap_long(uint32_t v)
-{
-    return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24);
-}
+#define dtohl(x) (x)
+#define dtohs(x) (x)
+#define htodl(x) (x)
+#define htods(x) (x)
 
-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 9d00602..953b859 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -17,6 +17,9 @@
 #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>
@@ -28,7 +31,9 @@
 
 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>
@@ -79,7 +84,9 @@
     }
 };
 
-
+// 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.
@@ -131,6 +138,9 @@
     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,
@@ -171,6 +181,9 @@
     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/Trace.h b/libutils/include/utils/Trace.h
index 4b9c91e..fec0ffa 100644
--- a/libutils/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -17,7 +17,12 @@
 #ifndef ANDROID_TRACE_H
 #define ANDROID_TRACE_H
 
-#if defined(__ANDROID__)
+#if defined(_WIN32)
+
+#define ATRACE_NAME(...)
+#define ATRACE_CALL()
+
+#else  // !_WIN32
 
 #include <stdint.h>
 
@@ -51,11 +56,6 @@
 
 }  // namespace android
 
-#else // !__ANDROID__
-
-#define ATRACE_NAME(...)
-#define ATRACE_CALL()
-
-#endif // __ANDROID__
+#endif  // _WIN32
 
 #endif // ANDROID_TRACE_H
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 0253f2f..2251479 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -184,3 +184,10 @@
     ],
     recovery_available: true,
 }
+
+cc_fuzz {
+    name: "libziparchive_fuzzer",
+    srcs: ["libziparchive_fuzzer.cpp"],
+    static_libs: ["libziparchive", "libbase", "libz", "liblog"],
+    host_supported: true,
+}
diff --git a/libziparchive/include/ziparchive/zip_archive.h b/libziparchive/include/ziparchive/zip_archive.h
index e3ac114..391cff9 100644
--- a/libziparchive/include/ziparchive/zip_archive.h
+++ b/libziparchive/include/ziparchive/zip_archive.h
@@ -114,7 +114,7 @@
 int32_t OpenArchiveFd(const int fd, const char* debugFileName, ZipArchiveHandle* handle,
                       bool assume_ownership = true);
 
-int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debugFileName,
+int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debugFileName,
                               ZipArchiveHandle* handle);
 /*
  * Close archive, releasing resources associated with it. This will
diff --git a/libziparchive/libziparchive_fuzzer.cpp b/libziparchive/libziparchive_fuzzer.cpp
new file mode 100644
index 0000000..75e7939
--- /dev/null
+++ b/libziparchive/libziparchive_fuzzer.cpp
@@ -0,0 +1,13 @@
+// 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/zip_archive.cc b/libziparchive/zip_archive.cc
index c95b035..3a552d8 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -178,7 +178,7 @@
 #endif
 }
 
-ZipArchive::ZipArchive(void* address, size_t length)
+ZipArchive::ZipArchive(const void* address, size_t length)
     : mapped_zip(address, length),
       close_file(false),
       directory_offset(0),
@@ -471,7 +471,7 @@
   return OpenArchiveInternal(archive, fileName);
 }
 
-int32_t OpenArchiveFromMemory(void* address, size_t length, const char* debug_file_name,
+int32_t OpenArchiveFromMemory(const void* address, size_t length, const char* debug_file_name,
                               ZipArchiveHandle* handle) {
   ZipArchive* archive = new ZipArchive(address, length);
   *handle = archive;
@@ -1152,7 +1152,7 @@
   return fd_;
 }
 
-void* MappedZipFile::GetBasePtr() const {
+const void* MappedZipFile::GetBasePtr() const {
   if (has_fd_) {
     ALOGW("Zip: MappedZipFile doesn't have a base pointer.");
     return nullptr;
@@ -1188,13 +1188,14 @@
       ALOGE("Zip: invalid offset: %" PRId64 ", data length: %" PRId64 "\n", off, data_length_);
       return false;
     }
-    memcpy(buf, static_cast<uint8_t*>(base_ptr_) + off, len);
+    memcpy(buf, static_cast<const uint8_t*>(base_ptr_) + off, len);
   }
   return true;
 }
 
-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;
+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;
   length_ = cd_size;
 }
 
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 30a1d72..60fdec0 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(void* address, size_t length)
+  explicit MappedZipFile(const 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;
 
-  void* GetBasePtr() const;
+  const void* GetBasePtr() const;
 
   off64_t GetFileLength() const;
 
@@ -117,7 +117,7 @@
 
   const int fd_;
 
-  void* const base_ptr_;
+  const void* const base_ptr_;
   const off64_t data_length_;
 };
 
@@ -129,7 +129,7 @@
 
   size_t GetMapLength() const { return length_; }
 
-  void Initialize(void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
+  void Initialize(const void* map_base_ptr, off64_t cd_start_offset, size_t cd_size);
 
  private:
   const uint8_t* base_ptr_;
@@ -177,7 +177,7 @@
   ZipStringOffset* hash_table;
 
   ZipArchive(const int fd, bool assume_ownership);
-  ZipArchive(void* address, size_t length);
+  ZipArchive(const void* address, size_t length);
   ~ZipArchive();
 
   bool InitializeCentralDirectory(off64_t cd_start_offset, size_t cd_size);
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 4c5ca03..e8e125b 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -1,3 +1,15 @@
+cc_defaults {
+    name: "stats_defaults",
+
+    product_variables: {
+        use_lmkd_stats_log: {
+            cflags: [
+                "-DLMKD_LOG_STATS"
+            ],
+        },
+    },
+}
+
 cc_binary {
     name: "lmkd",
 
@@ -15,13 +27,7 @@
     local_include_dirs: ["include"],
     cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
     init_rc: ["lmkd.rc"],
-    product_variables: {
-        use_lmkd_stats_log: {
-            cflags: [
-                "-DLMKD_LOG_STATS"
-            ],
-        },
-    },
+    defaults: ["stats_defaults"],
     logtags: ["event.logtags"],
 }
 
@@ -32,6 +38,7 @@
         "-Wall",
         "-Werror",
     ],
+    defaults: ["stats_defaults"],
     shared_libs: [
         "liblog",
     ],
diff --git a/lmkd/README.md b/lmkd/README.md
index 656a6ea..8a73692 100644
--- a/lmkd/README.md
+++ b/lmkd/README.md
@@ -60,6 +60,31 @@
                              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 065c6db..452f411 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: int
-# 2: long
+# 1: int32_t
+# 2: int64_t
 # 3: string
 # 4: list
 #
@@ -34,5 +34,5 @@
 #
 # TODO: generate ".java" and ".h" files with integer constants from this file.
 
-# for meminfo logs
-10195355 meminfo (MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(ION_heap|1),(ION_heap_pool|1),(CmaFree|1)
+# 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)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index d17da12..9de7ff7 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -31,6 +31,7 @@
 #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>
@@ -47,9 +48,7 @@
 #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,11 +78,14 @@
 #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 MEMINFO_LOG_TAG 10195355
+#define KILLINFO_LOG_TAG 10195355
 
 /* gid containing AID_SYSTEM required */
 #define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
@@ -110,15 +112,43 @@
  * PSI_WINDOW_SIZE_MS after the event happens.
  */
 #define PSI_WINDOW_SIZE_MS 1000
-/* Polling period after initial PSI signal */
-#define PSI_POLL_PERIOD_MS 10
-/* Poll for the duration of one window after initial PSI signal */
-#define PSI_POLL_COUNT (PSI_WINDOW_SIZE_MS / PSI_POLL_PERIOD_MS)
+/* 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
 
 #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;
@@ -149,6 +179,11 @@
 
 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;
@@ -159,7 +194,12 @@
 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 */
@@ -168,10 +208,33 @@
 
 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);
+    void (*handler)(int data, uint32_t events, struct polling_params *poll_params);
 };
 
 /* data required to handle socket events */
@@ -190,8 +253,11 @@
 /* vmpressure event handler data */
 static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
 
-/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket, 1 lmk events */
-#define MAX_EPOLL_EVENTS (2 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
+/*
+ * 1 ctrl listen socket, 2 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)
 static int epollfd;
 static int maxevents;
 
@@ -204,37 +270,99 @@
 static int lowmem_targets_size;
 
 /* Fields to parse in /proc/zoneinfo */
-enum zoneinfo_field {
-    ZI_NR_FREE_PAGES = 0,
-    ZI_NR_FILE_PAGES,
-    ZI_NR_SHMEM,
-    ZI_NR_UNEVICTABLE,
-    ZI_WORKINGSET_REFAULT,
-    ZI_HIGH,
-    ZI_FIELD_COUNT
+/* 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
 };
 
-static const char* const zoneinfo_field_names[ZI_FIELD_COUNT] = {
+static const char* const zoneinfo_zone_field_names[ZI_ZONE_FIELD_COUNT] = {
     "nr_free_pages",
-    "nr_file_pages",
-    "nr_shmem",
-    "nr_unevictable",
-    "workingset_refault",
+    "min",
+    "low",
     "high",
+    "present",
+    "nr_free_cma",
 };
 
-union zoneinfo {
+/* 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 {
     struct {
         int64_t nr_free_pages;
-        int64_t nr_file_pages;
-        int64_t nr_shmem;
-        int64_t nr_unevictable;
-        int64_t workingset_refault;
+        int64_t min;
+        int64_t low;
         int64_t high;
-        /* fields below are calculated rather than read from the file */
-        int64_t totalreserve_pages;
+        int64_t present;
+        int64_t nr_free_cma;
     } field;
-    int64_t arr[ZI_FIELD_COUNT];
+    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 workingset_refault;
+    } 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;
 };
 
 /* Fields to parse in /proc/meminfo */
@@ -310,6 +438,41 @@
     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,
@@ -324,6 +487,7 @@
 struct proc {
     struct adjslot_list asl;
     int pid;
+    int pidfd;
     uid_t uid;
     int oomadj;
     struct proc *pidhash_next;
@@ -334,11 +498,6 @@
     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))
@@ -362,6 +521,10 @@
 /* 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);
@@ -372,20 +535,25 @@
     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) {
-    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;
-        }
+    int i = find_field(cp, field_names, field_count);
+    if (i < 0) {
+        return NO_MATCH;
     }
-    return NO_MATCH;
+    *field_idx = i;
+    return parse_int64(ap, field) ? PARSE_SUCCESS : PARSE_FAIL;
 }
 
 /*
@@ -421,28 +589,50 @@
  * memory pressure to minimize file opening which by itself requires kernel
  * memory allocation and might result in a stall on memory stressed system.
  */
-static int reread_file(struct reread_data *data, char *buf, size_t buf_size) {
+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;
     ssize_t size;
 
     if (data->fd == -1) {
-        data->fd = open(data->filename, O_RDONLY | O_CLOEXEC);
-        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) {
             ALOGE("%s open: %s", data->filename, strerror(errno));
-            return -1;
+            return NULL;
         }
     }
 
-    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;
+    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;
     }
-    ALOG_ASSERT((size_t)size < buf_size - 1, "%s too large", data->filename);
     buf[size] = 0;
 
-    return 0;
+    return buf;
 }
 
 static struct proc *pid_lookup(int pid) {
@@ -514,6 +704,13 @@
         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;
 }
@@ -595,9 +792,62 @@
     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 void cmd_procprio(LMKD_CTRL_PACKET packet) {
     struct proc *procp;
-    char path[80];
+    char path[LINE_MAX];
     char val[20];
     int soft_limit_mult;
     struct lmk_procprio params;
@@ -634,6 +884,8 @@
     }
 
     if (use_inkernel_interface) {
+        stats_store_taskname(params.pid, proc_get_name(params.pid, path, sizeof(path)),
+                             kpoll_info.poll_fd);
         return;
     }
 
@@ -683,16 +935,27 @@
 
     procp = pid_lookup(params.pid);
     if (!procp) {
-            procp = malloc(sizeof(struct proc));
-            if (!procp) {
-                // Oh, the irony.  May need to rebuild our state.
+        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);
                 return;
             }
+        }
 
-            procp->pid = params.pid;
-            procp->uid = params.uid;
-            procp->oomadj = params.oomadj;
-            proc_insert(procp);
+        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->oomadj = params.oomadj;
+        proc_insert(procp);
     } else {
         proc_unslot(procp);
         procp->oomadj = params.oomadj;
@@ -703,11 +966,12 @@
 static void cmd_procremove(LMKD_CTRL_PACKET packet) {
     struct lmk_procremove params;
 
+    lmkd_pack_get_procremove(packet, &params);
     if (use_inkernel_interface) {
+        stats_remove_taskname(params.pid, kpoll_info.poll_fd);
         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.
@@ -721,6 +985,7 @@
     struct proc *next;
 
     if (use_inkernel_interface) {
+        stats_purge_tasknames();
         return;
     }
 
@@ -983,7 +1248,8 @@
     ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
 }
 
-static void ctrl_data_handler(int data, uint32_t events) {
+static void ctrl_data_handler(int data, uint32_t events,
+                              struct polling_params *poll_params __unused) {
     if (events & EPOLLIN) {
         ctrl_command_handler(data);
     }
@@ -998,7 +1264,8 @@
     return -1;
 }
 
-static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
+static void ctrl_connect_handler(int data __unused, uint32_t events __unused,
+                                 struct polling_params *poll_params __unused) {
     struct epoll_event epev;
     int free_dscock_idx = get_free_dsock();
 
@@ -1036,174 +1303,202 @@
     maxevents++;
 }
 
-#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) {
+/*
+ * /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;
     int64_t max = 0;
-    long long zoneval;
     char *save_ptr;
 
-    for (cp = strtok_r(cp, "(), ", &save_ptr); cp;
-         cp = strtok_r(NULL, "), ", &save_ptr)) {
-        zoneval = strtoll(cp, &cp, 0);
+    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);
         if (zoneval > max) {
             max = (zoneval > INT64_MAX) ? INT64_MAX : zoneval;
         }
+        zone->protection[zone_idx] = zoneval;
     }
-
-    return max;
+    zone->max_protection = max;
 }
 
-static bool zoneinfo_parse_line(char *line, union zoneinfo *zi) {
-    char *cp = line;
-    char *ap;
-    char *save_ptr;
-    int64_t val;
-    int field_idx;
+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;
 
-    cp = strtok_r(line, " ", &save_ptr);
-    if (!cp) {
-        return true;
-    }
-
-    if (!strcmp(cp, "protection:")) {
-        ap = strtok_r(NULL, ")", &save_ptr);
-    } else {
-        ap = strtok_r(NULL, " ", &save_ptr);
-    }
-
-    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);
+        cp = strtok_r(line, " ", &save_ptr);
+        if (!cp) {
+            return false;
         }
-        break;
-    case (PARSE_FAIL):
-    default:
-        return false;
+
+        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;
+        }
+
+        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 true;
+    return false;
 }
 
-static int zoneinfo_parse(union zoneinfo *zi) {
+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 struct reread_data file_data = {
         .filename = ZONEINFO_PATH,
         .fd = -1,
     };
-    char buf[PAGE_SIZE];
+    char *buf;
     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(union zoneinfo));
+    memset(zi, 0, sizeof(struct zoneinfo));
 
-    if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
+    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 (!zoneinfo_parse_line(line, zi)) {
-            ALOGE("%s parse error", file_data.filename);
-            return -1;
+        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;
+            }
         }
     }
-    zi->field.totalreserve_pages += zi->field.high;
+    if (!node) {
+        ALOGE("%s parse error", file_data.filename);
+        return -1;
+    }
+    node->zone_count = zone_idx + 1;
+    zi->node_count = node_idx + 1;
 
+    /* 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;
 }
 
-/* /prop/meminfo parsing routines */
+/* /proc/meminfo parsing routines */
 static bool meminfo_parse_line(char *line, union meminfo *mi) {
     char *cp = line;
     char *ap;
@@ -1235,13 +1530,13 @@
         .filename = MEMINFO_PATH,
         .fd = -1,
     };
-    char buf[PAGE_SIZE];
+    char *buf;
     char *save_ptr;
     char *line;
 
     memset(mi, 0, sizeof(union meminfo));
 
-    if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
+    if ((buf = reread_file(&file_data)) == NULL) {
         return -1;
     }
 
@@ -1258,7 +1553,70 @@
     return 0;
 }
 
-static void meminfo_log(union meminfo *mi) {
+/* /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 */
     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));
     }
@@ -1267,56 +1625,6 @@
     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)]);
 }
@@ -1379,25 +1687,117 @@
     closedir(d);
 }
 
-static int last_killed_pid = -1;
+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++;
+}
 
 /* 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) {
+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) {
     int pid = procp->pid;
+    int pidfd = procp->pidfd;
     uid_t uid = procp->uid;
     int tgid;
     char *taskname;
     int tasksize;
     int r;
     int result = -1;
-
-#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
+    struct memory_stat *mem_st;
+    char buf[LINE_MAX];
 
     tgid = proc_get_tgid(pid);
     if (tgid >= 0 && tgid != pid) {
@@ -1405,7 +1805,7 @@
         goto out;
     }
 
-    taskname = proc_get_name(pid);
+    taskname = proc_get_name(pid, buf, sizeof(buf));
     if (!taskname) {
         goto out;
     }
@@ -1415,50 +1815,49 @@
         goto out;
     }
 
-#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
+    mem_st = stats_read_memory_stat(per_app_memcg, pid, uid);
 
     TRACE_KILL_START(pid);
 
     /* CAP_KILL required */
-    r = kill(pid, SIGKILL);
-
-    set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
-
-    inc_killcnt(procp->oomadj);
-    ALOGE("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid, uid, procp->oomadj,
-          tasksize * page_k);
+    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();
 
-    last_killed_pid = pid;
-
     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;
-    } else {
-#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;
     }
 
+    set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
+
+    last_kill_tm = *tm;
+
+    inc_killcnt(procp->oomadj);
+
+    killinfo_log(procp, min_oom_score, tasksize, kill_reason, mi);
+
+    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);
+    } else {
+        ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid,
+              uid, procp->oomadj, tasksize * page_k);
+    }
+
+    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!
@@ -1472,13 +1871,11 @@
  * 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) {
+static int find_and_kill_process(int min_score_adj, int kill_reason, const char *kill_desc,
+                                 union meminfo *mi, struct timespec *tm) {
     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;
@@ -1490,15 +1887,13 @@
             if (!procp)
                 break;
 
-            killed_size = kill_one_process(procp, min_score_adj);
+            killed_size = kill_one_process(procp, min_score_adj, kill_reason, kill_desc, mi, tm);
             if (killed_size >= 0) {
-#ifdef LMKD_LOG_STATS
-                if (enable_stats_log && !lmk_state_change_start) {
+                if (!lmk_state_change_start) {
                     lmk_state_change_start = true;
-                    stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
+                    stats_write_lmk_state_changed(LMK_STATE_CHANGED,
                                                   LMK_STATE_CHANGE_START);
                 }
-#endif
                 break;
             }
         }
@@ -1507,11 +1902,9 @@
         }
     }
 
-#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);
+    if (lmk_state_change_start) {
+        stats_write_lmk_state_changed(LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
     }
-#endif
 
     return killed_size;
 }
@@ -1519,9 +1912,9 @@
 static int64_t get_memory_usage(struct reread_data *file_data) {
     int ret;
     int64_t mem_usage;
-    char buf[32];
+    char *buf;
 
-    if (reread_file(file_data, buf, sizeof(buf)) < 0) {
+    if ((buf = reread_file(file_data)) == NULL) {
         return -1;
     }
 
@@ -1573,33 +1966,289 @@
         level - 1 : level);
 }
 
-static bool is_kill_pending(void) {
-    char buf[24];
+enum zone_watermark {
+    WMARK_MIN = 0,
+    WMARK_LOW,
+    WMARK_HIGH,
+    WMARK_NONE
+};
 
-    if (last_killed_pid < 0) {
-        return false;
+struct zone_watermarks {
+    long high_wmark;
+    long low_wmark;
+    long min_wmark;
+};
+
+/*
+ * 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;
+
+    if (nr_free_pages < watermarks->min_wmark) {
+        return WMARK_MIN;
     }
-
-    snprintf(buf, sizeof(buf), "/proc/%d/", last_killed_pid);
-    if (access(buf, F_OK) == 0) {
-        return true;
+    if (nr_free_pages < watermarks->low_wmark) {
+        return WMARK_LOW;
     }
-
-    // reset last killed PID because there's nothing pending
-    last_killed_pid = -1;
-    return false;
+    if (nr_free_pages < watermarks->high_wmark) {
+        return WMARK_HIGH;
+    }
+    return WMARK_NONE;
 }
 
-static void mp_event_common(int data, uint32_t events __unused) {
+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) {
     int ret;
     unsigned long long evcount;
     int64_t mem_usage, memsw_usage;
     int64_t mem_pressure;
     enum vmpressure_level lvl;
     union meminfo mi;
-    union zoneinfo zi;
+    struct 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;
@@ -1634,20 +2283,40 @@
         }
     }
 
+    /* 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) {
-        // 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())) {
+    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()) {
             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) {
@@ -1664,7 +2333,7 @@
     if (use_minfree_levels) {
         int i;
 
-        other_free = mi.field.nr_free_pages - zi.field.totalreserve_pages;
+        other_free = mi.field.nr_free_pages - zi.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);
@@ -1746,12 +2415,10 @@
 do_kill:
     if (low_ram_device) {
         /* For Go devices kill only one task */
-        if (find_and_kill_process(level_oomadj[level]) == 0) {
+        if (find_and_kill_process(level_oomadj[level], -1, NULL, &mi, &curr_tm) == 0) {
             if (debug_process_killing) {
                 ALOGI("Nothing to kill");
             }
-        } else {
-            meminfo_log(&mi);
         }
     } else {
         int pages_freed;
@@ -1771,7 +2438,7 @@
             min_score_adj = level_oomadj[level];
         }
 
-        pages_freed = find_and_kill_process(min_score_adj);
+        pages_freed = find_and_kill_process(min_score_adj, -1, NULL, &mi, &curr_tm);
 
         if (pages_freed == 0) {
             /* Rate limit kill reports when nothing was reclaimed */
@@ -1779,20 +2446,15 @@
                 report_skip_count++;
                 return;
             }
-        } else {
-            /* If we killed anything, update the last killed timestamp. */
-            last_kill_tm = curr_tm;
         }
 
-        /* Log meminfo whenever we kill or when report rate limit allows */
-        meminfo_log(&mi);
-
+        /* Log whenever we kill or when report rate limit allows */
         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.field.totalreserve_pages * page_k,
+                zi.totalreserve_pages * page_k,
                 minfree * page_k, min_score_adj);
         } else {
             ALOGI("Reclaimed %ldkB at oom_adj %d",
@@ -1806,10 +2468,21 @@
 
         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) {
-    int fd = init_psi_monitor(psi_thresholds[level].stall_type,
+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,
         psi_thresholds[level].threshold_ms * US_PER_MS,
         PSI_WINDOW_SIZE_MS * US_PER_MS);
 
@@ -1817,7 +2490,7 @@
         return false;
     }
 
-    vmpressure_hinfo[level].handler = mp_event_common;
+    vmpressure_hinfo[level].handler = use_new_strategy ? mp_event_psi : mp_event_common;
     vmpressure_hinfo[level].data = level;
     if (register_psi_monitor(epollfd, fd, &vmpressure_hinfo[level]) < 0) {
         destroy_psi_monitor(fd);
@@ -1841,14 +2514,29 @@
 }
 
 static bool init_psi_monitors() {
-    if (!init_mp_psi(VMPRESS_LEVEL_LOW)) {
+    /*
+     * 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)) {
         return false;
     }
-    if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM)) {
+    if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM, use_new_strategy)) {
         destroy_mp_psi(VMPRESS_LEVEL_LOW);
         return false;
     }
-    if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL)) {
+    if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL, use_new_strategy)) {
         destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
         destroy_mp_psi(VMPRESS_LEVEL_LOW);
         return false;
@@ -1923,76 +2611,19 @@
     return false;
 }
 
-#ifdef LMKD_LOG_STATS
-static int kernel_poll_fd = -1;
-
-static void poll_kernel() {
-    if (kernel_poll_fd == -1) {
-        // not waiting
-        return;
-    }
-
-    while (1) {
-        char rd_buf[256];
-        int bytes_read =
-                TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
-        if (bytes_read <= 0) break;
-        rd_buf[bytes_read] = '\0';
-
-        int64_t pid;
-        int64_t uid;
-        int64_t group_leader_pid;
-        int64_t min_flt;
-        int64_t maj_flt;
-        int64_t rss_in_pages;
-        int16_t oom_score_adj;
-        int16_t min_score_adj;
-        int64_t starttime;
-        char* taskname = 0;
-        int fields_read = sscanf(rd_buf,
-                                 "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
-                                 " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
-                                 &pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
-                                 &oom_score_adj, &min_score_adj, &starttime, &taskname);
-
-        /* only the death of the group leader process is logged */
-        if (fields_read == 10 && group_leader_pid == pid) {
-            int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
-            stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, oom_score_adj,
-                                          min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
-                                          process_start_time_ns, min_score_adj);
-        }
-
-        free(taskname);
-    }
+static 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 struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
-
-static void init_poll_kernel() {
-    struct epoll_event epev;
-    kernel_poll_fd =
-            TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
-
-    if (kernel_poll_fd < 0) {
-        ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
-        return;
-    }
-
-    epev.events = EPOLLIN;
-    epev.data.ptr = (void*)&kernel_poll_hinfo;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
-        ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
-        close(kernel_poll_fd);
-        kernel_poll_fd = -1;
-    } else {
-        maxevents++;
-    }
-}
-#endif
-
 static int init(void) {
+    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;
 
@@ -2038,11 +2669,17 @@
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
-#ifdef LMKD_LOG_STATS
-        if (enable_stats_log) {
-            init_poll_kernel();
+        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++;
+            }
         }
-#endif
     } else {
         /* Try to use psi monitor first if kernel has it */
         use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
@@ -2069,37 +2706,110 @@
 
     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 event_handler_info* poll_handler = NULL;
-    struct timespec last_report_tm, curr_tm;
+    struct polling_params poll_params;
+    struct timespec curr_tm;
     struct epoll_event *evt;
     long delay = -1;
-    int polling = 0;
+
+    poll_params.poll_handler = NULL;
+    poll_params.update = POLLING_DO_NOT_CHANGE;
 
     while (1) {
         struct epoll_event events[maxevents];
         int nevents;
         int i;
 
-        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);
+        if (poll_params.poll_handler) {
+            bool poll_now;
 
             clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
-            if (get_time_diff_ms(&last_report_tm, &curr_tm) >= PSI_POLL_PERIOD_MS) {
-                polling--;
-                poll_handler->handler(poll_handler->data, 0);
-                last_report_tm = curr_tm;
+            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);
             }
         } else {
             /* Wait for events with no timeout */
@@ -2130,26 +2840,16 @@
 
         /* 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;
-                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);
-                }
+                call_handler(handler_info, &poll_params, evt->events);
             }
         }
     }
@@ -2185,14 +2885,20 @@
         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 =
-        property_get_int32("ro.lmk.swap_free_low_percentage", 10);
+    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));
 
-    ctx = create_android_logger(MEMINFO_LOG_TAG);
+    ctx = create_android_logger(KILLINFO_LOG_TAG);
 
-#ifdef LMKD_LOG_STATS
-    statslog_init(&log_ctx, &enable_stats_log);
-#endif
+    statslog_init();
 
     if (!init()) {
         if (!use_inkernel_interface) {
@@ -2221,9 +2927,7 @@
         mainloop();
     }
 
-#ifdef LMKD_LOG_STATS
-    statslog_destroy(&log_ctx);
-#endif
+    statslog_destroy();
 
     android_log_destroy(&ctx);
 
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index 0c230ae..c0fd6df 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -18,8 +18,32 @@
 #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;
@@ -27,34 +51,64 @@
     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(android_log_context ctx, int32_t code, int32_t state) {
-    assert(ctx != NULL);
+stats_write_lmk_state_changed(int32_t code, int32_t state) {
     int ret = -EINVAL;
-    if (!ctx) {
+
+    if (!enable_stats_log) {
         return ret;
     }
 
-    reset_log_context(ctx);
-
-    if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
+    assert(log_ctx != NULL);
+    if (!log_ctx) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, code)) < 0) {
+    reset_log_context(log_ctx);
+
+    if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, state)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
         return ret;
     }
 
-    return write_to_logger(ctx, LOG_ID_STATS);
+    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;
 }
 
 /**
@@ -62,65 +116,317 @@
  * Code: LMK_KILL_OCCURRED = 51
  */
 int
-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);
+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) {
     int ret = -EINVAL;
-    if (!ctx) {
+    if (!enable_stats_log) {
         return ret;
     }
-    reset_log_context(ctx);
+    if (!log_ctx) {
+        return ret;
+    }
+    reset_log_context(log_ctx);
 
-    if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, code)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, uid)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, uid)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_string8(ctx, (process_name == NULL) ? "" : process_name)) < 0) {
+    if ((ret = android_log_write_string8(log_ctx, (process_name == NULL) ? "" : process_name)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, oom_score)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, oom_score)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, pgfault)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgfault : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, pgmajfault)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgmajfault : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, rss_in_bytes)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->rss_in_bytes
+                                                       : tasksize * BYTES_IN_KILOBYTE)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, cache_in_bytes)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->cache_in_bytes : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, swap_in_bytes)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->swap_in_bytes : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int64(ctx, process_start_time_ns)) < 0) {
+    if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->process_start_time_ns
+                                                       : -1)) < 0) {
         return ret;
     }
 
-    if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
+    if ((ret = android_log_write_int32(log_ctx, min_oom_score)) < 0) {
         return ret;
     }
 
-    return write_to_logger(ctx, LOG_ID_STATS);
+    return write_to_logger(log_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 2edba7a..a09c7dd 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -18,6 +18,7 @@
 #define _STATSLOG_H_
 
 #include <assert.h>
+#include <inttypes.h>
 #include <stats_event_list.h>
 #include <stdbool.h>
 #include <sys/cdefs.h>
@@ -26,6 +27,20 @@
 
 __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
@@ -35,37 +50,17 @@
 #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;
 
-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_init();
 
-    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;
-};
+void statslog_destroy();
 
 #define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
 #define PROC_STAT_FILE_PATH "/proc/%d/stat"
@@ -78,18 +73,63 @@
  * Code: LMK_STATE_CHANGED = 54
  */
 int
-stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state);
+stats_write_lmk_state_changed(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(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);
+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 */
 
 __END_DECLS
 
diff --git a/logcat/Android.bp b/logcat/Android.bp
index f1b18b2..e6b0c7d 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -50,16 +50,13 @@
     ],
 }
 
-cc_prebuilt_binary {
+sh_binary {
     name: "logpersist.start",
-    srcs: ["logpersist"],
+    src: "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 e2711af..4dcb338 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -32,6 +32,7 @@
 #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>
@@ -158,8 +159,22 @@
                          enum helpType showHelp, const char* fmt, ...)
     __printflike(3, 4);
 
-static int openLogFile(const char* pathname) {
-    return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+#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, 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 void close_output(android_logcat_context_internal* context) {
@@ -192,6 +207,7 @@
             if (context->fds[1] == context->output_fd) {
                 context->fds[1] = -1;
             }
+            posix_fadvise(context->output_fd, 0, 0, POSIX_FADV_DONTNEED);
             close(context->output_fd);
         }
         context->output_fd = -1;
@@ -276,7 +292,7 @@
         }
     }
 
-    context->output_fd = openLogFile(context->outputFileName);
+    context->output_fd = openLogFile(context->outputFileName, context->logRotateSizeKBytes);
 
     if (context->output_fd < 0) {
         logcat_panic(context, HELP_FALSE, "couldn't open output file");
@@ -398,7 +414,7 @@
 
     close_output(context);
 
-    context->output_fd = openLogFile(context->outputFileName);
+    context->output_fd = openLogFile(context->outputFileName, context->logRotateSizeKBytes);
 
     if (context->output_fd < 0) {
         logcat_panic(context, HELP_FALSE, "couldn't open output file");
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 26c9de3..e986184 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -52,7 +52,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:-1024} -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r ${logd.logpersistd.rotate_kbytes:-2048} -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/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 5a375ec..d9cc0db 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -143,10 +143,6 @@
 
 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:");
@@ -160,7 +156,7 @@
     }
     auto search = denial_to_bug.find(scontext + tcontext + tclass);
     if (search != denial_to_bug.end()) {
-        bug_num->assign(" b/" + search->second);
+        bug_num->assign(" " + search->second);
     } else {
         bug_num->assign("");
     }
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 9cbc7c4..ba05a06 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, privileged, sameTid);
+        curr = element->flushTo(reader, this, sameTid);
 
         if (curr == element->FLUSH_ERROR) {
             return curr;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 82c4fb9..5c43e18 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -244,14 +244,10 @@
     return retval;
 }
 
-log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent,
-                                   bool privileged, bool lastSame) {
-    struct logger_entry_v4 entry;
+log_time LogBufferElement::flushTo(SocketClient* reader, LogBuffer* parent, bool lastSame) {
+    struct logger_entry_v4 entry = {};
 
-    memset(&entry, 0, sizeof(struct logger_entry_v4));
-
-    entry.hdr_size = privileged ? sizeof(struct logger_entry_v4)
-                                : sizeof(struct logger_entry_v3);
+    entry.hdr_size = sizeof(struct logger_entry_v4);
     entry.lid = mLogId;
     entry.pid = mPid;
     entry.tid = mTid;
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index da4991b..fd790e4 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _LOGD_LOG_BUFFER_ELEMENT_H__
-#define _LOGD_LOG_BUFFER_ELEMENT_H__
+#pragma once
 
 #include <stdatomic.h>
 #include <stdint.h>
@@ -96,8 +95,5 @@
     }
 
     static const log_time FLUSH_ERROR;
-    log_time flushTo(SocketClient* writer, LogBuffer* parent, bool privileged,
-                     bool lastSame);
+    log_time flushTo(SocketClient* writer, LogBuffer* parent, bool lastSame);
 };
-
-#endif
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 443570f..ba61042 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -41,8 +41,7 @@
     }
 
     // + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
-    char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) +
-                LOGGER_ENTRY_MAX_PAYLOAD + 1];
+    char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
     struct iovec iov = { buffer, sizeof(buffer) - 1 };
 
     alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
diff --git a/logd/fuzz/Android.bp b/logd/fuzz/Android.bp
new file mode 100644
index 0000000..3215b24
--- /dev/null
+++ b/logd/fuzz/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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
new file mode 100644
index 0000000..be4c7c3
--- /dev/null
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../LogBuffer.h"
+#include "../LogTimes.h"
+
+namespace android {
+struct LogInput {
+  public:
+    log_id_t log_id;  // char
+    log_time realtime;
+    uid_t uid;
+    pid_t pid;
+    pid_t tid;
+};
+
+int write_log_messages(const uint8_t* data, size_t* data_left, LogBuffer* log_buffer) {
+    const LogInput* logInput = reinterpret_cast<const LogInput*>(data);
+    data += sizeof(LogInput);
+    *data_left -= sizeof(LogInput);
+
+    uint8_t tag_length = data[0] % 32;
+    uint8_t msg_length = data[1] % 32;
+    if (tag_length < 2 || msg_length < 2) {
+        // Not enough data for tag and message
+        return 0;
+    }
+
+    data += 2 * sizeof(uint8_t);
+    *data_left -= 2 * sizeof(uint8_t);
+
+    if (*data_left < tag_length + msg_length) {
+        // Not enough data for tag and message
+        return 0;
+    }
+
+    // We need nullterm'd strings
+    char* msg = new char[tag_length + msg_length + 2];
+    char* msg_only = msg + tag_length + 1;
+    memcpy(msg, data, tag_length);
+    msg[tag_length] = '\0';
+    memcpy(msg_only, data, msg_length);
+    msg_only[msg_length] = '\0';
+    data += tag_length + msg_length;
+    *data_left -= tag_length + 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,
+                    tag_length + msg_length + 2);
+    delete[] msg;
+    return 1;
+}
+
+// Because system/core/logd/main.cpp redefines this.
+void prdebug(char const* fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+}
+
+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;
+
+    log_buffer.enableStatistics();
+    // 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(data, &data_left, &log_buffer)) {
+            return 0;
+        }
+    }
+
+    log_id_for_each(i) { log_buffer.clear(i); }
+    return 0;
+}
+}  // namespace android
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index b6c33d7..80625a7 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -349,86 +349,6 @@
 }
 #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
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index c378646..8851a47 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -13,11 +13,12 @@
     name: "liblogwrap",
     defaults: ["logwrapper_defaults"],
     recovery_available: true,
-    srcs: ["logwrap.c"],
+    srcs: ["logwrap.cpp"],
     shared_libs: [
         "libcutils",
         "liblog",
     ],
+    header_libs: ["libbase_headers"],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
 }
@@ -31,9 +32,10 @@
     defaults: ["logwrapper_defaults"],
     local_include_dirs: ["include"],
     srcs: [
-        "logwrap.c",
-        "logwrapper.c",
+        "logwrap.cpp",
+        "logwrapper.cpp",
     ],
+    header_libs: ["libbase_headers"],
     shared_libs: ["libcutils", "liblog"],
 }
 
@@ -54,10 +56,10 @@
 // ========================================================
 
 cc_benchmark {
-    name: "android_fork_execvp_ext_benchmark",
+    name: "logwrap_fork_execvp_benchmark",
     defaults: ["logwrapper_defaults"],
     srcs: [
-        "android_fork_execvp_ext_benchmark.cpp",
+        "logwrap_fork_execvp_benchmark.cpp",
     ],
     shared_libs: [
         "libbase",
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index d3538b3..cb40ee2 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -15,23 +15,11 @@
  * limitations under the License.
  */
 
-#ifndef __LIBS_LOGWRAP_H
-#define __LIBS_LOGWRAP_H
-
-#include <stdbool.h>
-#include <stdint.h>
-
-__BEGIN_DECLS
+#pragma once
 
 /*
  * 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
@@ -40,10 +28,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.
- *   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)
+ *   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.
  *   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
@@ -54,8 +42,6 @@
  *           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
@@ -65,29 +51,11 @@
  *
  */
 
-/* Values for the log_target parameter android_fork_execvp_ext() */
+/* Values for the log_target parameter logwrap_fork_execvp() */
 #define LOG_NONE        0
 #define LOG_ALOG        1
 #define LOG_KLOG        2
 #define LOG_FILE        4
 
-// 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 */
+int logwrap_fork_execvp(int argc, const char* const* argv, int* status, bool forward_signals,
+                        int log_target, bool abbreviated, const char* file_path);
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.cpp
similarity index 62%
rename from logwrapper/logwrap.c
rename to logwrapper/logwrap.cpp
index 8621993..5337801 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.cpp
@@ -19,7 +19,6 @@
 #include <libgen.h>
 #include <poll.h>
 #include <pthread.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -28,26 +27,31 @@
 #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
 
@@ -56,7 +60,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;
@@ -69,7 +73,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;
@@ -79,7 +83,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;
@@ -90,19 +94,17 @@
 struct log_info {
     int log_target;
     char klog_fmt[MAX_KLOG_TAG * 2];
-    char *btag;
+    const 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) {
@@ -116,20 +118,18 @@
     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 == NULL) {
+    if (e_buf->buf == nullptr) {
         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 = MIN(line_len, e_buf->buf_size - e_buf->write);
+    cnt = std::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, char *line) {
+static void do_log_line(struct log_info* log_info, const 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,9 +184,8 @@
  * 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;
 
@@ -212,17 +211,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 = malloc(BEGINNING_BUF_SIZE);
+    new_buf = static_cast<char*>(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 = malloc(ENDING_BUF_SIZE);
+    new_buf = static_cast<char*>(malloc(ENDING_BUF_SIZE));
     if (new_buf) {
         a_buf->e_buf.buf = new_buf;
         a_buf->e_buf.alloc_len = ENDING_BUF_SIZE;
@@ -230,23 +229,22 @@
     }
 }
 
-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) {
@@ -269,14 +267,13 @@
      * 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 = malloc(a_buf->e_buf.alloc_len);
+        char* nbuf = static_cast<char*>(malloc(a_buf->e_buf.alloc_len));
         if (!nbuf) {
             return;
         }
@@ -289,15 +286,54 @@
     }
 }
 
-static int parent(const char *tag, int parent_read, pid_t pid,
-        int *chld_sts, int log_target, bool abbreviated, char *file_path) {
+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) {
     int status = 0;
     char buffer[4096];
     struct pollfd poll_fds[] = {
-        [0] = {
-            .fd = parent_read,
-            .events = POLLIN,
-        },
+            {
+                    .fd = parent_read,
+                    .events = POLLIN,
+            },
     };
     int rc = 0;
     int fd;
@@ -308,11 +344,16 @@
     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 = (char*) tag;
+        log_info.btag = tag;
     }
 
     if (abbreviated && (log_target == LOG_NONE)) {
@@ -323,8 +364,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) {
@@ -347,15 +388,16 @@
     log_info.abbreviated = abbreviated;
 
     while (!found_child) {
-        if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) {
+        int timeout = received_messages ? -1 : 1000;
+        if (TEMP_FAILURE_RETRY(poll(poll_fds, arraysize(poll_fds), timeout)) < 0) {
             ERROR("poll failed\n");
             rc = -1;
             goto err_poll;
         }
 
         if (poll_fds[0].revents & POLLIN) {
-            sz = TEMP_FAILURE_RETRY(
-                read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
+            received_messages = true;
+            sz = TEMP_FAILURE_RETRY(read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
 
             sz += b;
             // Log one line at a time
@@ -396,10 +438,20 @@
             }
         }
 
-        if (poll_fds[0].revents & POLLHUP) {
+        if (!received_messages || (poll_fds[0].revents & POLLHUP)) {
             int ret;
+            sigset_t oldset;
 
-            ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+            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));
             if (ret < 0) {
                 rc = errno;
                 ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
@@ -408,22 +460,29 @@
             if (ret > 0) {
                 found_child = true;
             }
+
+            if (forward_signals) {
+                if (found_child) {
+                    restore_signal_handlers();
+                }
+                unblock_signals(&oldset);
+            }
         }
     }
 
-    if (chld_sts != NULL) {
+    if (chld_sts != nullptr) {
         *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 */
@@ -432,21 +491,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:
@@ -460,33 +519,24 @@
     return rc;
 }
 
-static void child(int argc, char* argv[]) {
+static void child(int argc, const char* const* argv) {
     // create null terminated argv_child array
     char* argv_child[argc + 1];
-    memcpy(argv_child, argv, argc * sizeof(char *));
-    argv_child[argc] = NULL;
+    memcpy(argv_child, argv, argc * sizeof(char*));
+    argv_child[argc] = nullptr;
 
     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 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) {
+int logwrap_fork_execvp(int argc, const char* const* argv, int* status, bool forward_signals,
+                        int log_target, bool abbreviated, const char* file_path) {
     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");
@@ -494,7 +544,7 @@
     }
 
     /* Use ptty instead of socketpair so that STDOUT is not buffered */
-    parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR));
+    parent_ptty = TEMP_FAILURE_RETRY(posix_openpt(O_RDWR | O_CLOEXEC));
     if (parent_ptty < 0) {
         ERROR("Cannot create parent ptty\n");
         rc = -1;
@@ -503,33 +553,34 @@
 
     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;
     }
 
-    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;
+    if (forward_signals) {
+        // Block these signals until we have the child pid and our signal handlers set up.
+        block_signals(&oldset);
     }
 
-    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);
-        pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+        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));
+        }
         close(parent_ptty);
 
         dup2(child_ptty, 1);
@@ -538,27 +589,23 @@
 
         child(argc, argv);
     } else {
-        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);
+        if (forward_signals) {
+            setup_signal_handlers(pid);
+            unblock_signals(&oldset);
         }
 
-        rc = parent(argv[0], parent_ptty, pid, status, log_target,
-                    abbreviated, file_path);
+        rc = parent(argv[0], parent_ptty, pid, status, log_target, abbreviated, file_path,
+                    forward_signals);
+
+        if (forward_signals) {
+            restore_signal_handlers();
+        }
     }
 
-    if (ignore_int_quit) {
-        sigaction(SIGINT, &intact, NULL);
-        sigaction(SIGQUIT, &quitact, NULL);
-    }
 err_fork:
-    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
-err_child_ptty:
+    if (forward_signals) {
+        unblock_signals(&oldset);
+    }
 err_ptty:
     close(parent_ptty);
 err_open:
diff --git a/logwrapper/android_fork_execvp_ext_benchmark.cpp b/logwrapper/logwrap_fork_execvp_benchmark.cpp
similarity index 81%
rename from logwrapper/android_fork_execvp_ext_benchmark.cpp
rename to logwrapper/logwrap_fork_execvp_benchmark.cpp
index 1abd932..b2d0c71 100644
--- a/logwrapper/android_fork_execvp_ext_benchmark.cpp
+++ b/logwrapper/logwrap_fork_execvp_benchmark.cpp
@@ -23,9 +23,7 @@
     const char* argv[] = {"/system/bin/echo", "hello", "world"};
     const int argc = 3;
     while (state.KeepRunning()) {
-        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 */);
+        int rc = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_NONE, false, nullptr);
         CHECK_EQ(0, rc);
     }
 }
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.cpp
similarity index 62%
rename from logwrapper/logwrapper.c
rename to logwrapper/logwrapper.cpp
index 33454c6..7118d12 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.cpp
@@ -24,27 +24,26 @@
 #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[]) {
@@ -69,7 +68,7 @@
                 break;
             case '?':
             default:
-              usage();
+                usage();
         }
     }
     argc -= optind;
@@ -79,8 +78,7 @@
         usage();
     }
 
-    rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
-                                 log_target, abbreviated, NULL, NULL, 0);
+    rc = logwrap_fork_execvp(argc, &argv[0], &status, true, log_target, abbreviated, nullptr);
     if (!rc) {
         if (WIFEXITED(status))
             rc = WEXITSTATUS(status);
@@ -89,8 +87,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/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index 51c1226..aa02a3a 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -1,7 +1,6 @@
 cc_defaults {
     name: "propertyinfoserializer_defaults",
     host_supported: true,
-    vendor_available: true,
     cpp_std: "experimental",
     cppflags: [
         "-Wall",
@@ -9,8 +8,8 @@
         "-Werror",
     ],
     static_libs: [
-        "libpropertyinfoparser",
         "libbase",
+        "libpropertyinfoparser",
     ],
 }
 
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index df1d929..e1bb02f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -58,15 +58,6 @@
 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)
@@ -245,12 +236,10 @@
 ifeq ($(_enforce_vndk_at_runtime),true)
 
 # for VNDK enforced devices
-LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
-include $(BUILD_SYSTEM)/base_rules.mk
-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
+# 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)
 
 else ifeq ($(_enforce_vndk_lite_at_runtime),true)
 
@@ -271,36 +260,6 @@
 
 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
 #
@@ -334,6 +293,15 @@
 LOCAL_MODULE_STEM := ld.config.txt
 include $(BUILD_PREBUILT)
 
+# 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
+
 #######################################
 # llndk.libraries.txt
 include $(CLEAR_VARS)
@@ -342,13 +310,13 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(call module-installed-files-or-guess,$(LLNDK_LIBRARIES),)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_LLNDK_LIBRARIES), \
-		echo $(lib).so >> $@;)
+		echo $(lib) >> $@;)
 
 #######################################
 # vndksp.libraries.txt
@@ -358,13 +326,13 @@
 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): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
-		echo $(lib).so >> $@;)
+		echo $(lib) >> $@;)
 
 #######################################
 # vndkcore.libraries.txt
@@ -374,13 +342,13 @@
 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_CORE_LIBRARIES := $(VNDK_CORE_LIBRARIES)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_CORE_LIBRARIES), \
-		echo $(lib).so >> $@;)
+		echo $(lib) >> $@;)
 
 #######################################
 # vndkprivate.libraries.txt
@@ -390,13 +358,13 @@
 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_PRIVATE_LIBRARIES := $(VNDK_PRIVATE_LIBRARIES)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_PRIVATE_LIBRARIES), \
-		echo $(lib).so >> $@;)
+		echo $(lib) >> $@;)
 
 #######################################
 # sanitizer.libraries.txt
@@ -423,6 +391,22 @@
 		echo $(lib) >> $@;)
 
 #######################################
+# vndkcorevariant.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndkcorevariant.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_CORE_VARIANT_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_USING_CORE_VARIANT_LIBRARIES),.vendor)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_VNDK_CORE_VARIANT_LIBRARIES), \
+		echo $(lib) >> $@;)
+
+#######################################
 # adb_debug.prop in debug ramdisk
 include $(CLEAR_VARS)
 LOCAL_MODULE := adb_debug.prop
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
new file mode 100644
index 0000000..85d2786
--- /dev/null
+++ b/rootdir/avb/Android.bp
@@ -0,0 +1,20 @@
+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/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index e598f05..a99756a 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -42,29 +42,29 @@
 # APEX related namespaces.
 ###############################################################################
 
-additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv
+additional.namespaces = art,conscrypt,media,neuralnetworks,resolv
 
 # Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace. And then, if the
-# shared library cannot be loaded from the runtime namespace either, the
+# to load the shared library from the art namespace. And then, if the
+# shared library cannot be loaded from the art 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,neuralnetworks
-namespace.default.asan.links = runtime,resolv,neuralnetworks
-namespace.default.link.runtime.shared_libs  = libandroidicu.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+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.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.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
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.art.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
@@ -75,27 +75,25 @@
 namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
 ###############################################################################
-# "runtime" APEX namespace
+# "art" APEX namespace
 #
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the ART APEX.
+# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
-namespace.runtime.isolated = true
+namespace.art.isolated = true
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
-namespace.runtime.visible = true
+namespace.art.visible = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
+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.runtime.link.default.allow_all_shared_libs = true
+namespace.art.link.default.allow_all_shared_libs = true
+namespace.art.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
 ###############################################################################
 # "media" APEX namespace
@@ -138,8 +136,8 @@
 
 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.links = art,default
+namespace.conscrypt.link.art.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
@@ -181,6 +179,7 @@
 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
 
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index e1da587..5c87843 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -1,806 +1,3 @@
-# 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 = /%SYSTEM_EXT%/bin/
-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,neuralnetworks,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
-# 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 += /%SYSTEM_EXT%/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${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 += /%SYSTEM_EXT%/${LIB}
-namespace.default.permitted.paths += /%PRODUCT%/${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 += /%SYSTEM_EXT%/framework
-namespace.default.permitted.paths += /%SYSTEM_EXT%/app
-namespace.default.permitted.paths += /%SYSTEM_EXT%/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 += /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/%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.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 += /%SYSTEM_EXT%/${LIB}
-namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/framework
-namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/app
-namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/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 += /mnt/expand
-namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
-namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
-
-# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-# If a shared library or an executable requests a shared library that
-# cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace. And then, if the
-# shared library cannot be loaded from the runtime namespace either, the
-# dynamic linker tries to load the shared library from the resolv namespace.
-# Finally, if all attempts fail, the dynamic linker returns an error.
-namespace.default.links = runtime,resolv,neuralnetworks
-namespace.default.link.runtime.shared_libs  = libandroidicu.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.runtime.shared_libs += libicui18n.so
-namespace.default.link.runtime.shared_libs += libicuuc.so
-namespace.default.link.runtime.shared_libs += libnativebridge.so
-namespace.default.link.runtime.shared_libs += libnativehelper.so
-namespace.default.link.runtime.shared_libs += libnativeloader.so
-
-# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.runtime.shared_libs += libpac.so
-namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-# When libnetd_resolv.so can't be found in the default namespace, search for it
-# in the resolv namespace. Don't allow any other libraries from the resolv namespace
-# to be loaded in the default namespace.
-namespace.default.link.resolv.shared_libs = libnetd_resolv.so
-
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "runtime" APEX namespace
-#
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.runtime.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.runtime.visible = true
-
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
-# Need allow_all_shared_libs because libart.so can dlopen oat files in
-# /system/framework and /data.
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
-namespace.runtime.link.default.allow_all_shared_libs = true
-
-###############################################################################
-# "media" APEX namespace
-#
-# This namespace is for libraries within the media APEX.
-###############################################################################
-namespace.media.isolated = true
-namespace.media.visible = true
-
-namespace.media.search.paths = /apex/com.android.media/${LIB}
-namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
-
-namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
-namespace.media.asan.permitted.paths = /apex/com.android.media/${LIB}/extractors
-
-namespace.media.links = default,neuralnetworks
-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%
-
-# 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
-
-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
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.sphal.visible = true
-
-namespace.sphal.search.paths  = /odm/${LIB}
-namespace.sphal.search.paths += /vendor/${LIB}
-namespace.sphal.search.paths += /vendor/${LIB}/hw
-
-namespace.sphal.permitted.paths  = /odm/${LIB}
-namespace.sphal.permitted.paths += /vendor/${LIB}
-namespace.sphal.permitted.paths += /system/vendor/${LIB}
-
-namespace.sphal.asan.search.paths  = /data/asan/odm/${LIB}
-namespace.sphal.asan.search.paths +=           /odm/${LIB}
-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,neuralnetworks
-
-# 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%
-
-# LLNDK library moved into apex
-namespace.sphal.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-
-###############################################################################
-# "rs" namespace
-#
-# This namespace is exclusively for Renderscript internal libraries.
-# This namespace has slightly looser restriction than the vndk namespace because
-# of the genuine characteristics of Renderscript; /data is in the permitted path
-# to load the compiled *.so file and libmediandk.so can be used here.
-###############################################################################
-namespace.rs.isolated = true
-namespace.rs.visible = true
-
-namespace.rs.search.paths  = /odm/${LIB}/vndk-sp
-namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
-namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
-namespace.rs.search.paths += /odm/${LIB}
-namespace.rs.search.paths += /vendor/${LIB}
-
-namespace.rs.permitted.paths  = /odm/${LIB}
-namespace.rs.permitted.paths += /vendor/${LIB}
-namespace.rs.permitted.paths += /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,neuralnetworks
-
-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%
-
-# LLNDK library moved into apex
-namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-
-###############################################################################
-# "vndk" namespace
-#
-# 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
-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,neuralnetworks
-
-# 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
-
-# LLNDK library moved into apex
-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 += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
-
-###############################################################################
-# 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,neuralnetworks,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,neuralnetworks
-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%
-
-# LLNDK library moved into apex
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "runtime" APEX namespace
-#
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.runtime.isolated = true
-
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.links = system
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
-namespace.runtime.link.system.allow_all_shared_libs = true
-
-
-###############################################################################
-# "vndk" namespace
-#
-# This namespace is where VNDK and VNDK-SP libraries are loaded for
-# 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,neuralnetworks
-
-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%
-
-# LLNDK library moved into apex
-namespace.vndk.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "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 += /%SYSTEM_EXT%/${LIB}
-namespace.system.search.paths += /%PRODUCT%/${LIB}
-
-namespace.system.asan.search.paths  = /data/asan/system/${LIB}
-namespace.system.asan.search.paths +=           /system/${LIB}
-namespace.system.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
-namespace.system.asan.search.paths +=           /%SYSTEM_EXT%/${LIB}
-namespace.system.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
-namespace.system.asan.search.paths +=           /%PRODUCT%/${LIB}
-
-namespace.system.links = runtime
-namespace.system.link.runtime.shared_libs  = libdexfile_external.so
-namespace.system.link.runtime.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.system.link.runtime.shared_libs += libicui18n.so
-namespace.system.link.runtime.shared_libs += libicuuc.so
-namespace.system.link.runtime.shared_libs += libnativebridge.so
-namespace.system.link.runtime.shared_libs += libnativehelper.so
-namespace.system.link.runtime.shared_libs += libnativeloader.so
-# Workaround for b/124772622
-namespace.system.link.runtime.shared_libs += libandroidicu.so
-namespace.system.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-###############################################################################
-# "vndk_in_system" namespace
-#
-# This namespace is where no-vendor-variant VNDK libraries are loaded for a
-# vendor process.  Note that we do not simply export these libraries from
-# "system" namespace, because in some case both the core variant and the
-# vendor variant of a VNDK library may be loaded.  In such case, we do not
-# want to eliminate double-loading because doing so means the global states
-# of the library would be shared.
-#
-# Only the no-vendor-variant VNDK libraries are whitelisted in this namespace.
-# This is to ensure that we do not load libraries needed by no-vendor-variant
-# VNDK libraries into vndk_in_system namespace.
-###############################################################################
-namespace.vndk_in_system.isolated = true
-namespace.vndk_in_system.visible = true
-
-# The search paths here should be kept the same as that of the 'system'
-# namespace.
-namespace.vndk_in_system.search.paths  = /system/${LIB}
-namespace.vndk_in_system.search.paths += /%SYSTEM_EXT%/${LIB}
-namespace.vndk_in_system.search.paths += /%PRODUCT%/${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/%SYSTEM_EXT%/${LIB}
-namespace.vndk_in_system.asan.search.paths +=           /%SYSTEM_EXT%/${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.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,neuralnetworks
-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.vndk_in_system.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 = system
-namespace.neuralnetworks.link.system.shared_libs  = libc.so
-namespace.neuralnetworks.link.system.shared_libs += libcgrouprc.so
-namespace.neuralnetworks.link.system.shared_libs += libdl.so
-namespace.neuralnetworks.link.system.shared_libs += liblog.so
-namespace.neuralnetworks.link.system.shared_libs += libm.so
-namespace.neuralnetworks.link.system.shared_libs += libnativewindow.so
-namespace.neuralnetworks.link.system.shared_libs += libsync.so
-namespace.neuralnetworks.link.system.shared_libs += libvndksupport.so
-
-###############################################################################
-# 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,neuralnetworks
-
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.default.visible = true
-
-namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /odm/${LIB}
-namespace.default.search.paths += /vendor/${LIB}
-
-namespace.default.asan.search.paths  = /data/asan/system/${LIB}
-namespace.default.asan.search.paths +=           /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/odm/${LIB}
-namespace.default.asan.search.paths +=           /odm/${LIB}
-namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
-namespace.default.asan.search.paths +=           /vendor/${LIB}
-
-# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-namespace.default.links = runtime,resolv,neuralnetworks
-namespace.default.link.runtime.shared_libs  = libandroidicu.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
-# TODO(b/120786417 or b/134659294): libicuuc.so and libicui18n.so are kept for app compat.
-namespace.default.link.runtime.shared_libs += libicui18n.so
-namespace.default.link.runtime.shared_libs += libicuuc.so
-namespace.default.link.runtime.shared_libs += libnativebridge.so
-namespace.default.link.runtime.shared_libs += libnativehelper.so
-namespace.default.link.runtime.shared_libs += libnativeloader.so
-
-# TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.runtime.shared_libs += libpac.so
-namespace.default.link.runtime.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-
-namespace.default.link.resolv.shared_libs = libnetd_resolv.so
-namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
-
-###############################################################################
-# "runtime" APEX namespace
-#
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
-###############################################################################
-namespace.runtime.isolated = true
-# Visible to allow links to be created at runtime, e.g. through
-# android_link_namespaces in libnativeloader.
-namespace.runtime.visible = true
-
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
-# TODO(b/130340935): Use a dynamically created linker namespace similar to
-# classloader-namespace for oat files, and tighten this up.
-namespace.runtime.link.default.allow_all_shared_libs = true
-
-###############################################################################
-# "media" APEX namespace
-#
-# This namespace is for libraries within the media APEX.
-###############################################################################
-namespace.media.isolated = true
-namespace.media.visible = true
-
-namespace.media.search.paths = /apex/com.android.media/${LIB}
-namespace.media.asan.search.paths = /apex/com.android.media/${LIB}
-
-namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
-namespace.media.asan.permitted.paths = /apex/com.android.media/${LIB}/extractors
-
-namespace.media.links = default,neuralnetworks
-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
-
-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
-
-###############################################################################
-# "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 += libsync.so
-namespace.neuralnetworks.link.default.shared_libs += libvndksupport.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 += /%SYSTEM_EXT%/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
+# 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
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 4beabd6..9c9f4a9 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -35,7 +35,7 @@
 dir.system = /data
 
 [system]
-additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
+additional.namespaces = art,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -68,23 +68,23 @@
 # Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 # If a shared library or an executable requests a shared library that
 # cannot be loaded into the default namespace, the dynamic linker tries
-# to load the shared library from the runtime namespace. And then, if the
-# shared library cannot be loaded from the runtime namespace either, the
+# to load the shared library from the art namespace. And then, if the
+# shared library cannot be loaded from the art 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,neuralnetworks
-namespace.default.link.runtime.shared_libs  = libandroidicu.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+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.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.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
 
 # TODO(b/122876336): Remove libpac.so once it's migrated to Webview
-namespace.default.link.runtime.shared_libs += libpac.so
+namespace.default.link.art.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
@@ -95,27 +95,25 @@
 namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
 ###############################################################################
-# "runtime" APEX namespace
+# "art" APEX namespace
 #
-# This namespace pulls in externally accessible libs from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+# 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.
 ###############################################################################
-namespace.runtime.isolated = true
+namespace.art.isolated = true
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
-namespace.runtime.visible = true
+namespace.art.visible = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
+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.runtime.link.default.allow_all_shared_libs = true
+namespace.art.link.default.allow_all_shared_libs = true
+namespace.art.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
 ###############################################################################
 # "media" APEX namespace
@@ -150,12 +148,13 @@
 
 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.links = art,default
+namespace.conscrypt.link.art.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
@@ -175,6 +174,7 @@
 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
@@ -346,9 +346,10 @@
 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
@@ -357,7 +358,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = runtime,neuralnetworks
+additional.namespaces = art,neuralnetworks
 
 namespace.default.isolated = false
 
@@ -399,38 +400,35 @@
 namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
 namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
 
-namespace.default.links = runtime,neuralnetworks
-namespace.default.link.runtime.shared_libs  = libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+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.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.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
 # Workaround for b/124772622
-namespace.default.link.runtime.shared_libs += libandroidicu.so
+namespace.default.link.art.shared_libs += libandroidicu.so
 
 # LLNDK library moved into apex
 namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
 ###############################################################################
-# "runtime" APEX namespace
+# "art" APEX namespace
 #
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the ART APEX.
+# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
-namespace.runtime.isolated = true
+namespace.art.isolated = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
+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.runtime.link.default.allow_all_shared_libs = true
+namespace.art.link.default.allow_all_shared_libs = true
 
 ###############################################################################
 # "neuralnetworks" APEX namespace
@@ -449,9 +447,10 @@
 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 native tests that need access to both system and vendor
@@ -460,7 +459,7 @@
 # includes the requisite namespace setup for APEXes.
 ###############################################################################
 [unrestricted]
-additional.namespaces = runtime,media,conscrypt,resolv,neuralnetworks
+additional.namespaces = art,media,conscrypt,resolv,neuralnetworks
 
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
@@ -478,20 +477,19 @@
 namespace.default.asan.search.paths +=           /vendor/${LIB}
 
 # Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-namespace.default.links = runtime,resolv,neuralnetworks
-namespace.default.link.runtime.shared_libs  = libandroidicu.so
-namespace.default.link.runtime.shared_libs += libdexfile_external.so
-namespace.default.link.runtime.shared_libs += libdexfiled_external.so
+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.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.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
 
 # 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.art.shared_libs += libpac.so
 
 namespace.default.link.resolv.shared_libs = libnetd_resolv.so
 
@@ -499,24 +497,22 @@
 namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
 
 ###############################################################################
-# "runtime" APEX namespace
+# "art" APEX namespace
 #
-# This namespace exposes externally accessible libraries from the Runtime APEX.
-# Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
+# This namespace exposes externally accessible libraries from the ART APEX.
+# Keep in sync with the "art" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
-namespace.runtime.isolated = true
+namespace.art.isolated = true
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
-namespace.runtime.visible = true
+namespace.art.visible = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.links = default
+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.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
 # "media" APEX namespace
@@ -551,11 +547,12 @@
 
 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.links = art,default
+namespace.conscrypt.link.art.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
@@ -573,6 +570,7 @@
 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
@@ -591,9 +589,10 @@
 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.
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 27e855f..405f5a9 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
+libneuralnetworks.so nopreload
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
deleted file mode 100644
index 4fee15f..0000000
--- a/rootdir/fsverity_init.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/system/bin/sh
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Enforce fsverity signature checking
-echo 1 > /proc/sys/fs/verity/require_signatures
-
-# Load all keys
-for cert in /product/etc/security/fsverity/*.der; do
-  /system/bin/mini-keyctl padd asymmetric fsv_product .fs-verity < "$cert" ||
-    log -p e -t fsverity_init "Failed to load $cert"
-done
-
-DEBUGGABLE=$(getprop ro.debuggable)
-if [ $DEBUGGABLE != "1" ]; then
-  # Prevent future key links to .fs-verity keyring
-  /system/bin/mini-keyctl restrict_keyring .fs-verity ||
-    log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
-fi
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 17f6596..50005d9 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -5,7 +5,7 @@
     export ANDROID_ASSETS /system/app
     export ANDROID_DATA /data
     export ANDROID_STORAGE /storage
-    export ANDROID_RUNTIME_ROOT /apex/com.android.art
+    export ANDROID_ART_ROOT /apex/com.android.art
     export ANDROID_I18N_ROOT /apex/com.android.i18n
     export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
     export EXTERNAL_STORAGE /sdcard
diff --git a/rootdir/init.rc b/rootdir/init.rc
index afa1a86..c5bf1603 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -52,6 +52,40 @@
     # 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
 
@@ -124,13 +158,17 @@
     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
 
-    # tmpfs place for BORINGSSL_self_test() to remember whether it has run
-    mkdir /dev/boringssl 0755 root root
-    mkdir /dev/boringssl/selftest 0755 root root
-
     # Storage views to support runtime permissions
     mkdir /mnt/runtime 0700 root root
     mkdir /mnt/runtime/default 0755 root root
@@ -435,7 +473,6 @@
     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
@@ -452,10 +489,6 @@
     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
 
@@ -653,6 +686,8 @@
   # 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
@@ -721,11 +756,6 @@
     write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
 
     # Permissions for System Server and daemons.
-    chown radio system /sys/android_power/state
-    chown radio system /sys/android_power/request_state
-    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 system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate
@@ -789,6 +819,9 @@
 
     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
@@ -831,6 +864,9 @@
 
 on property:sys.boot_completed=1
     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
 
 # system server cannot write to /proc/sys files,
 # and chown/chmod does not work for /proc/sys/ entries.
@@ -882,6 +918,17 @@
 on init && property:ro.debuggable=1
     start console
 
-service flash_recovery /system/bin/install-recovery.sh
-    class main
-    oneshot
+on userspace-reboot
+  # 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 0
+
+on userspace-reboot-resume
+  # TODO(b/135984674): remount userdata and reset checkpointing
+  trigger nonencrypted
+  trigger post-fs-data
+  trigger zygote-start
+  trigger early-boot
+  trigger boot
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index bf3fb42..9adbcba 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,7 +5,6 @@
     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 1bab588..f6149c9 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,7 +5,6 @@
     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 6fa210a..0e69b16 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,7 +5,6 @@
     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 48461ec..3e80168 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,7 +5,6 @@
     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 451f5ad..9c2cdf2 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 dbe60e5..44f7b65 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -38,8 +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.txt
-vndkprivate_libraries_file := $(library_lists_dir)/vndkprivate.libraries.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
 ifeq ($(my_vndk_use_core_variant),true)
 vndk_using_core_variant_libraries_file := $(library_lists_dir)/vndk_using_core_variant.libraries.$(vndk_version).txt
@@ -70,19 +70,30 @@
 # /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): output file to write the list of libraries to
-define write-libs-to-file
-$(2): PRIVATE_LIBRARIES := $(1)
-$(2):
-	echo -n > $$@ && $$(foreach lib,$$(PRIVATE_LIBRARIES),echo $$(lib).so >> $$@;)
+# $(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
-$(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)))
+
+# $(1): list of libraries
+# $(2): suffix to add to each library
+# $(3): 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) >> $$@;)
+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)))
 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)
 
@@ -117,7 +128,7 @@
 deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
   $(vndkprivate_libraries_file)
 ifeq ($(check_backward_compatibility),true)
-deps += $(compatibility_check_script)
+deps += $(compatibility_check_script) $(wildcard prebuilts/vndk/*/*/configs/ld.config.*.txt)
 endif
 ifeq ($(my_vndk_use_core_variant),true)
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE := $(vndk_using_core_variant_libraries_file)
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
index b5fc6bf..ec2ba12 100644
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -17,6 +17,7 @@
 
 #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>
@@ -46,6 +47,9 @@
 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 {
@@ -79,6 +83,45 @@
     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) {
@@ -472,11 +515,11 @@
 Return<void> TrustyKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
                                            const hidl_vec<KeyParameter>& inParams,
                                            const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
-    (void)authToken;
+    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(inParams));
+    request.additional_params.Reinitialize(KmParamSet(extendedParams));
 
     BeginOperationResponse response;
     impl_->BeginOperation(request, &response);
@@ -496,16 +539,16 @@
                                             const HardwareAuthToken& authToken,
                                             const VerificationToken& verificationToken,
                                             update_cb _hidl_cb) {
-    (void)authToken;
     (void)verificationToken;
     UpdateOperationRequest request;
     UpdateOperationResponse response;
     hidl_vec<KeyParameter> resultParams;
     hidl_vec<uint8_t> resultBlob;
+    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
     uint32_t resultConsumed = 0;
 
     request.op_handle = operationHandle;
-    request.additional_params.Reinitialize(KmParamSet(inParams));
+    request.additional_params.Reinitialize(KmParamSet(extendedParams));
 
     size_t inp_size = input.size();
     size_t ser_size = request.SerializedSize();
@@ -537,13 +580,13 @@
                                             const HardwareAuthToken& authToken,
                                             const VerificationToken& verificationToken,
                                             finish_cb _hidl_cb) {
-    (void)authToken;
     (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(inParams));
+    request.additional_params.Reinitialize(KmParamSet(extendedParams));
 
     FinishOperationResponse response;
     impl_->FinishOperation(request, &response);
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
new file mode 100644
index 0000000..aa30707
--- /dev/null
+++ b/trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml
@@ -0,0 +1,11 @@
+<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/Android.bp b/trusty/keymaster/Android.bp
index 3a9beaf..f32a69e 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -136,4 +136,6 @@
         "libkeymaster4",
         "android.hardware.keymaster@4.0"
     ],
+
+    vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
 }