Merge "Fix ext4/metadata/udc issue"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6f6481f..f6ef906 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -82,3 +82,9 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin/charger)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services.img)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/product_services)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 66d0c92..cc1978d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,6 +4,9 @@
       "name": "adbd_test"
     },
     {
+      "name": "CtsInitTestCases"
+    },
+    {
       "name": "debuggerd_test"
     },
     {
@@ -13,10 +16,10 @@
       "name": "fs_mgr_vendor_overlay_test"
     },
     {
-      "name": "init_tests"
+      "name": "libbase_test"
     },
     {
-      "name": "libbase_test"
+      "name": "libpackagelistparser_test"
     },
     {
       "name": "libprocinfo_test"
diff --git a/adb/Android.bp b/adb/Android.bp
index 114eb2a..57872b0 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -27,6 +27,7 @@
         "-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",
 
@@ -124,7 +125,8 @@
     "adb_trace.cpp",
     "adb_unique_fd.cpp",
     "adb_utils.cpp",
-    "fdevent.cpp",
+    "fdevent/fdevent.cpp",
+    "fdevent/fdevent_poll.cpp",
     "services.cpp",
     "sockets.cpp",
     "socket_spec.cpp",
@@ -140,11 +142,15 @@
     "sysdeps/posix/network.cpp",
 ]
 
+libadb_linux_srcs = [
+    "fdevent/fdevent_epoll.cpp",
+]
+
 libadb_test_srcs = [
     "adb_io_test.cpp",
     "adb_listeners_test.cpp",
     "adb_utils_test.cpp",
-    "fdevent_test.cpp",
+    "fdevent/fdevent_test.cpp",
     "socket_spec_test.cpp",
     "socket_test.cpp",
     "sysdeps_test.cpp",
@@ -168,12 +174,11 @@
 
     target: {
         linux: {
-            srcs: ["client/usb_linux.cpp"],
+            srcs: ["client/usb_linux.cpp"] + libadb_linux_srcs,
         },
         darwin: {
             srcs: ["client/usb_osx.cpp"],
         },
-
         not_windows: {
             srcs: libadb_posix_srcs,
         },
@@ -220,6 +225,7 @@
     target: {
         windows: {
             enabled: true,
+            ldflags: ["-municode"],
             shared_libs: ["AdbWinApi"],
         },
     },
@@ -268,22 +274,33 @@
         "client/console.cpp",
         "client/adb_install.cpp",
         "client/line_printer.cpp",
+        "client/fastdeploy.cpp",
+        "client/fastdeploycallbacks.cpp",
         "shell_service_protocol.cpp",
     ],
 
+    generated_headers: [
+        "bin2c_fastdeployagent",
+        "bin2c_fastdeployagentscript"
+    ],
+
     static_libs: [
         "libadb_host",
+        "libandroidfw",
         "libbase",
         "libcutils",
         "libcrypto_utils",
         "libcrypto",
+        "libfastdeploy_host",
         "libdiagnose_usb",
         "liblog",
         "libmdnssd",
+        "libprotobuf-cpp-lite",
         "libusb",
         "libutils",
         "liblog",
-        "libcutils",
+        "libziparchive",
+        "libz",
     ],
 
     stl: "libc++_static",
@@ -293,10 +310,6 @@
     // will violate ODR
     shared_libs: [],
 
-    required: [
-        "deploypatchgenerator",
-    ],
-
     // Archive adb, adb.exe.
     dist: {
         targets: [
@@ -332,7 +345,7 @@
     // libminadbd wants both, as it's used to build native tests.
     compile_multilib: "both",
 
-    srcs: libadb_srcs + libadb_posix_srcs + [
+    srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
         "daemon/auth.cpp",
         "daemon/jdwp_service.cpp",
     ],
@@ -344,6 +357,7 @@
     generated_headers: ["platform_tools_version"],
 
     static_libs: [
+        "libadbconnection_server",
         "libdiagnose_usb",
     ],
 
@@ -395,6 +409,7 @@
     ],
 
     static_libs: [
+        "libadbconnection_server",
         "libadbd_core",
         "libdiagnose_usb",
     ],
@@ -433,7 +448,6 @@
             shared_libs: [
                 "libbootloader_message",
                 "libmdnssd",
-                "libext4_utils",
                 "libfec",
                 "libfs_mgr",
                 "libselinux",
@@ -531,6 +545,7 @@
     },
 
     static_libs: [
+        "libadbconnection_server",
         "libadbd",
         "libadbd_services",
         "libasyncio",
@@ -654,3 +669,90 @@
         },
     },
 }
+
+// Note: using pipe for xxd to control the variable name generated
+// the default name used by xxd is the path to the input file.
+java_genrule {
+    name: "bin2c_fastdeployagent",
+    out: ["deployagent.inc"],
+    srcs: [":deployagent"],
+    cmd: "(echo 'unsigned char kDeployAgent[] = {' && xxd -i <$(in) && echo '};') > $(out)",
+}
+
+genrule {
+    name: "bin2c_fastdeployagentscript",
+    out: ["deployagentscript.inc"],
+    srcs: ["fastdeploy/deployagent/deployagent.sh"],
+    cmd: "(echo 'unsigned char kDeployAgentScript[] = {' && xxd -i <$(in) && echo '};') > $(out)",
+}
+
+cc_library_host_static {
+    name: "libfastdeploy_host",
+    defaults: ["adb_defaults"],
+    srcs: [
+        "fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp",
+        "fastdeploy/deploypatchgenerator/patch_utils.cpp",
+        "fastdeploy/proto/ApkEntry.proto",
+    ],
+    static_libs: [
+        "libadb_host",
+        "libandroidfw",
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "liblog",
+        "libmdnssd",
+        "libusb",
+        "libutils",
+        "libziparchive",
+        "libz",
+    ],
+    stl: "libc++_static",
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
+    target: {
+        windows: {
+            enabled: true,
+            shared_libs: ["AdbWinApi"],
+        },
+    },
+}
+
+cc_test_host {
+    name: "fastdeploy_test",
+    defaults: ["adb_defaults"],
+    srcs: [
+        "fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp",
+        "fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
+    ],
+    static_libs: [
+        "libadb_host",
+        "libandroidfw",
+        "libbase",
+        "libcutils",
+        "libcrypto_utils",
+        "libcrypto",
+        "libdiagnose_usb",
+        "libfastdeploy_host",
+        "liblog",
+        "libmdnssd",
+        "libprotobuf-cpp-lite",
+        "libusb",
+        "libutils",
+        "libziparchive",
+        "libz",
+    ],
+    target: {
+        windows: {
+            enabled: true,
+            shared_libs: ["AdbWinApi"],
+        },
+    },
+    data: [
+        "fastdeploy/testdata/rotating_cube-release.apk",
+    ],
+}
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 24d4292..7dff1b8 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1243,11 +1243,7 @@
     // TODO: Switch handle_forward_request to string_view.
     std::string service_str(service);
     if (handle_forward_request(
-                service_str.c_str(),
-                [=](std::string* error) {
-                    return acquire_one_transport(type, serial, transport_id, nullptr, error);
-                },
-                reply_fd)) {
+                service_str.c_str(), [=](std::string* error) { return s->transport; }, reply_fd)) {
         return HostRequestResult::Handled;
     }
 
diff --git a/adb/adb.h b/adb/adb.h
index 3a6f059..352b2fe 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -26,7 +26,7 @@
 #include <android-base/macros.h>
 
 #include "adb_trace.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
 #include "socket.h"
 #include "types.h"
 #include "usb.h"
diff --git a/adb/adb_listeners_test.cpp b/adb/adb_listeners_test.cpp
index b697769..a7e2dea 100644
--- a/adb/adb_listeners_test.cpp
+++ b/adb/adb_listeners_test.cpp
@@ -21,7 +21,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
 #include "sysdeps.h"
 #include "transport.h"
 
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index cf5fbc8..d1910f1 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -320,6 +320,10 @@
 }
 
 std::string GetLogFilePath() {
+    // https://issuetracker.google.com/112588493
+    const char* path = getenv("ANDROID_ADB_LOG_PATH");
+    if (path) return path;
+
 #if defined(_WIN32)
     const char log_name[] = "adb.log";
     WCHAR temp_path[MAX_PATH];
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index faad03d..66cba12 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <charconv>
 #include <condition_variable>
 #include <mutex>
 #include <string>
@@ -112,33 +113,17 @@
 // Base-10 stroll on a string_view.
 template <typename T>
 inline bool ParseUint(T* result, std::string_view str, std::string_view* remaining = nullptr) {
-    if (str.empty() || !isdigit(str[0])) {
+    T value;
+    const auto res = std::from_chars(str.begin(), str.end(), value);
+    if (res.ec != std::errc{}) {
         return false;
     }
-
-    T value = 0;
-    std::string_view::iterator it;
-    constexpr T max = std::numeric_limits<T>::max();
-    for (it = str.begin(); it != str.end() && isdigit(*it); ++it) {
-        if (value > max / 10) {
-            return false;
-        }
-
-        value *= 10;
-
-        T digit = *it - '0';
-        if (value > max - digit) {
-            return false;
-        }
-
-        value += digit;
+    if (res.ptr != str.end() && !remaining) {
+        return false;
+    }
+    if (remaining) {
+        *remaining = std::string_view(res.ptr, str.end() - res.ptr);
     }
     *result = value;
-    if (remaining) {
-        *remaining = str.substr(it - str.begin());
-    } else {
-      return it == str.end();
-    }
-
     return true;
 }
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 7e408a8..d91ae35 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -149,7 +149,8 @@
     return false;
 }
 
-static int _adb_connect(std::string_view service, TransportId* transport, std::string* error) {
+static int _adb_connect(std::string_view service, TransportId* transport, std::string* error,
+                        bool force_switch = false) {
     LOG(DEBUG) << "_adb_connect: " << service;
     if (service.empty() || service.size() > MAX_PAYLOAD) {
         *error = android::base::StringPrintf("bad service name length (%zd)", service.size());
@@ -164,7 +165,7 @@
         return -2;
     }
 
-    if (!service.starts_with("host")) {
+    if (!service.starts_with("host") || force_switch) {
         std::optional<TransportId> transport_result = switch_socket_transport(fd.get(), error);
         if (!transport_result) {
             return -1;
@@ -323,7 +324,8 @@
     return result;
 }
 
-int adb_connect(TransportId* transport, std::string_view service, std::string* error) {
+int adb_connect(TransportId* transport, std::string_view service, std::string* error,
+                bool force_switch_device) {
     LOG(DEBUG) << "adb_connect: service: " << service;
 
     // Query the adb server's version.
@@ -336,7 +338,7 @@
         return 0;
     }
 
-    unique_fd fd(_adb_connect(service, transport, error));
+    unique_fd fd(_adb_connect(service, transport, error, force_switch_device));
     if (fd == -1) {
         D("_adb_connect error: %s", error->c_str());
     } else if(fd == -2) {
@@ -398,9 +400,15 @@
 }
 
 bool adb_get_feature_set(FeatureSet* feature_set, std::string* error) {
-    std::string result;
-    if (adb_query(format_host_command("features"), &result, error)) {
-        *feature_set = StringToFeatureSet(result);
+    static FeatureSet* features = nullptr;
+    if (!features) {
+        std::string result;
+        if (adb_query(format_host_command("features"), &result, error)) {
+            features = new FeatureSet(StringToFeatureSet(result));
+        }
+    }
+    if (features) {
+        *feature_set = *features;
         return true;
     }
     feature_set->clear();
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index fe1e584..ba53041 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -34,7 +34,11 @@
 int adb_connect(std::string_view service, std::string* _Nonnull error);
 
 // Same as above, except returning the TransportId for the service that we've connected to.
-int adb_connect(TransportId* _Nullable id, std::string_view service, std::string* _Nonnull error);
+// force_switch_device forces the function to attempt to select a device, even if the service
+// string appears to be a host: service (for use with host services that are device specific, like
+// forward).
+int adb_connect(TransportId* _Nullable id, std::string_view service, std::string* _Nonnull error,
+                bool force_switch_device = false);
 
 // Kill the currently running adb server, if it exists.
 bool adb_kill_server();
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 16fa215..a6e8998 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -16,6 +16,7 @@
 
 #include "adb_install.h"
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -40,18 +41,31 @@
 static constexpr int kFastDeployMinApi = 24;
 #endif
 
+namespace {
+
+enum InstallMode {
+    INSTALL_DEFAULT,
+    INSTALL_PUSH,
+    INSTALL_STREAM,
+};
+
+}
+
 static bool can_use_feature(const char* feature) {
     FeatureSet features;
     std::string error;
     if (!adb_get_feature_set(&features, &error)) {
         fprintf(stderr, "error: %s\n", error.c_str());
-        return true;
+        return false;
     }
     return CanUseFeature(features, feature);
 }
 
-static bool use_legacy_install() {
-    return !can_use_feature(kFeatureCmd);
+static InstallMode best_install_mode() {
+    if (can_use_feature(kFeatureCmd)) {
+        return INSTALL_STREAM;
+    }
+    return INSTALL_PUSH;
 }
 
 static bool is_apex_supported() {
@@ -112,7 +126,7 @@
 }
 
 int uninstall_app(int argc, const char** argv) {
-    if (use_legacy_install()) {
+    if (best_install_mode() == INSTALL_PUSH) {
         return uninstall_app_legacy(argc, argv);
     }
     return uninstall_app_streamed(argc, argv);
@@ -200,32 +214,49 @@
             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::string cmd = "exec:cmd package";
+        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) {
-            cmd += " " + escape_arg(std::string(*argv++));
+            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 += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+        cmd_args.push_back("-S");
+        cmd_args.push_back(
+                android::base::StringPrintf("%" PRIu64, static_cast<uint64_t>(sb.st_size)));
 
         if (is_apex) {
-            cmd += " --apex";
+            cmd_args.push_back("--apex");
         }
 
-        unique_fd remote_fd(adb_connect(cmd, &error));
+        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;
         }
 
-        char buf[BUFSIZ];
         copy_to_file(local_fd.get(), remote_fd.get());
-        read_status_line(remote_fd.get(), buf, sizeof(buf));
 
+        char buf[BUFSIZ];
+        read_status_line(remote_fd.get(), buf, sizeof(buf));
         if (!strncmp("Success", buf, 7)) {
             fputs(buf, stdout);
             return 0;
@@ -256,8 +287,7 @@
 
     int result = -1;
     std::vector<const char*> apk_file = {argv[last_apk]};
-    std::string apk_dest =
-            "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
+    std::string apk_dest = "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
 
     if (use_fastdeploy == true) {
 #if defined(ENABLE_FASTDEPLOY)
@@ -292,11 +322,7 @@
 
 int install_app(int argc, const char** argv) {
     std::vector<int> processedArgIndicies;
-    enum installMode {
-        INSTALL_DEFAULT,
-        INSTALL_PUSH,
-        INSTALL_STREAM
-    } installMode = INSTALL_DEFAULT;
+    InstallMode installMode = INSTALL_DEFAULT;
     bool use_fastdeploy = false;
     bool is_reinstall = false;
     bool use_localagent = false;
@@ -337,14 +363,10 @@
     }
 
     if (installMode == INSTALL_DEFAULT) {
-        if (use_legacy_install()) {
-            installMode = INSTALL_PUSH;
-        } else {
-            installMode = INSTALL_STREAM;
-        }
+        installMode = best_install_mode();
     }
 
-    if (installMode == INSTALL_STREAM && use_legacy_install() == true) {
+    if (installMode == INSTALL_STREAM && best_install_mode() == INSTALL_PUSH) {
         error_exit("Attempting to use streaming install on unsupported device");
     }
 
@@ -409,7 +431,8 @@
             android::base::EndsWithIgnoreCase(file, ".dm") ||
             android::base::EndsWithIgnoreCase(file, ".fsv_sig")) {
             struct stat sb;
-            if (stat(file, &sb) != -1) total_size += sb.st_size;
+            if (stat(file, &sb) == -1) perror_exit("failed to stat \"%s\"", file);
+            total_size += sb.st_size;
             first_apk = i;
         } else {
             break;
@@ -419,7 +442,7 @@
     if (first_apk == -1) error_exit("need APK file on command line");
 
     std::string install_cmd;
-    if (use_legacy_install()) {
+    if (best_install_mode() == INSTALL_PUSH) {
         install_cmd = "exec:pm";
     } else {
         install_cmd = "exec:cmd package";
@@ -459,13 +482,13 @@
     }
 
     // Valid session, now stream the APKs
-    int success = 1;
+    bool success = true;
     for (int i = first_apk; i < argc; i++) {
         const char* file = argv[i];
         struct stat sb;
         if (stat(file, &sb) == -1) {
-            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-            success = 0;
+            fprintf(stderr, "adb: failed to stat \"%s\": %s\n", file, strerror(errno));
+            success = false;
             goto finalize_session;
         }
 
@@ -476,8 +499,8 @@
 
         unique_fd local_fd(adb_open(file, O_RDONLY | O_CLOEXEC));
         if (local_fd < 0) {
-            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-            success = 0;
+            fprintf(stderr, "adb: failed to open \"%s\": %s\n", file, strerror(errno));
+            success = false;
             goto finalize_session;
         }
 
@@ -485,7 +508,7 @@
         unique_fd remote_fd(adb_connect(cmd, &error));
         if (remote_fd < 0) {
             fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-            success = 0;
+            success = false;
             goto finalize_session;
         }
 
@@ -493,15 +516,15 @@
         read_status_line(remote_fd.get(), buf, sizeof(buf));
 
         if (strncmp("Success", buf, 7)) {
-            fprintf(stderr, "adb: failed to write %s\n", file);
+            fprintf(stderr, "adb: failed to write \"%s\"\n", file);
             fputs(buf, stderr);
-            success = 0;
+            success = false;
             goto finalize_session;
         }
     }
 
 finalize_session:
-    // Commit session if we streamed everything okay; otherwise abandon
+    // Commit session if we streamed everything okay; otherwise abandon.
     std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
                                                       success ? "commit" : "abandon", session_id);
     {
@@ -512,14 +535,16 @@
         }
         read_status_line(fd.get(), buf, sizeof(buf));
     }
+    if (!success) return EXIT_FAILURE;
 
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
+    if (strncmp("Success", buf, 7)) {
+        fprintf(stderr, "adb: failed to finalize session\n");
+        fputs(buf, stderr);
+        return EXIT_FAILURE;
     }
-    fprintf(stderr, "adb: failed to finalize session\n");
-    fputs(buf, stderr);
-    return EXIT_FAILURE;
+
+    fputs(buf, stdout);
+    return EXIT_SUCCESS;
 }
 
 int install_multi_package(int argc, const char** argv) {
@@ -542,7 +567,7 @@
 
     if (first_package == -1) error_exit("need APK or APEX files on command line");
 
-    if (use_legacy_install()) {
+    if (best_install_mode() == INSTALL_PUSH) {
         fprintf(stderr, "adb: multi-package install is not supported on this device\n");
         return EXIT_FAILURE;
     }
@@ -739,6 +764,20 @@
 }
 
 int delete_device_file(const std::string& filename) {
-    std::string cmd = "rm -f " + escape_arg(filename);
-    return send_shell_command(cmd);
+    // http://b/17339227 "Sideloading a Readonly File Results in a Prompt to
+    // Delete" caused us to add `-f` here, to avoid the equivalent of the `-i`
+    // prompt that you get from BSD rm (used in Android 5) if you have a
+    // non-writable file and stdin is a tty (which is true for old versions of
+    // adbd).
+    //
+    // Unfortunately, `rm -f` requires Android 4.3, so that workaround broke
+    // earlier Android releases. This was reported as http://b/37704384 "adb
+    // install -r passes invalid argument to rm on Android 4.1" and
+    // http://b/37035817 "ADB Fails: rm failed for -f, No such file or
+    // directory".
+    //
+    // Testing on a variety of devices and emulators shows that redirecting
+    // stdin is sufficient to avoid the pseudo-`-i`, and works on toolbox,
+    // BSD, and toybox versions of rm.
+    return send_shell_command("rm " + escape_arg(filename) + " </dev/null");
 }
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 7211f72..fe2bdfe 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -96,9 +96,8 @@
         " version                  show version num\n"
         "\n"
         "networking:\n"
-        " connect HOST[:PORT]      connect to a device via TCP/IP [default port=5555]\n"
-        " disconnect [HOST[:PORT]]\n"
-        "     disconnect from given TCP/IP device [default port=5555], or all\n"
+        " connect HOST[:PORT]      connect to a device via TCP/IP\n"
+        " disconnect [[HOST]:PORT] disconnect from given TCP/IP device, or all\n"
         " forward --list           list all forward socket connections\n"
         " forward [--no-rebind] LOCAL REMOTE\n"
         "     forward socket connection using:\n"
@@ -128,9 +127,9 @@
         " pull [-a] REMOTE... LOCAL\n"
         "     copy files/dirs from device\n"
         "     -a: preserve file timestamp and mode\n"
-        " sync [all|data|odm|oem|product_services|product|system|vendor]\n"
+        " sync [all|data|odm|oem|product|system|system_ext|vendor]\n"
         "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
-        "     -l: list but don't copy\n"
+        "     -l: list files that would be copied, but don't copy them\n"
         "\n"
         "shell:\n"
         " shell [-e ESCAPE] [-n] [-Tt] [-x] [COMMAND...]\n"
@@ -154,6 +153,7 @@
         "     -d: allow version code downgrade (debuggable packages only)\n"
         "     -p: partial application install (install-multiple only)\n"
         "     -g: grant all runtime permissions\n"
+        "     --abi ABI: override platform's default ABI\n"
         "     --instant: cause the app to be installed as an ephemeral install app\n"
         "     --no-streaming: always push APK to device and invoke Package Manager as separate steps\n"
         "     --streaming: force streaming APK directly into Package Manager\n"
@@ -165,6 +165,7 @@
 #ifndef _WIN32
         "     --local-agent: locate agent files from local source build (instead of SDK location)\n"
 #endif
+        "     (See also `adb shell pm help` for more options.)\n"
         //TODO--installlog <filename>
         " uninstall [-k] PACKAGE\n"
         "     remove this app package from the device\n"
@@ -356,7 +357,7 @@
 }
 
 void copy_to_file(int inFd, int outFd) {
-    std::vector<char> buf(32 * 1024);
+    std::vector<char> buf(64 * 1024);
     int len;
     long total = 0;
     int old_stdin_mode = -1;
@@ -775,17 +776,16 @@
         error_exit("abb is not supported by the device");
     }
 
+    optind = 1;  // argv[0] is always "abb", so set `optind` appropriately.
+
     // Defaults.
     constexpr char escape_char = '~';  // -e
     constexpr bool use_shell_protocol = true;
     constexpr auto shell_type_arg = kShellServiceArgRaw;
     constexpr bool empty_command = false;
 
-    std::string service_string("abb:");
-    for (auto i = optind; i < argc; ++i) {
-        service_string.append(argv[i]);
-        service_string.push_back(ABB_ARG_DELIMETER);
-    }
+    std::vector<const char*> args(argv + optind, argv + argc);
+    std::string service_string = "abb:" + android::base::Join(args, ABB_ARG_DELIMETER);
 
     D("abb -e 0x%x [%*.s]\n", escape_char, static_cast<int>(service_string.size()),
       service_string.data());
@@ -1616,13 +1616,13 @@
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "connect")) {
-        if (argc != 2) error_exit("usage: adb connect <host>[:<port>]");
+        if (argc != 2) error_exit("usage: adb connect HOST[:PORT>]");
 
         std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "disconnect")) {
-        if (argc > 2) error_exit("usage: adb disconnect [<host>[:<port>]]");
+        if (argc > 2) error_exit("usage: adb disconnect [HOST[:PORT]]");
 
         std::string query = android::base::StringPrintf("host:disconnect:%s",
                                                         (argc == 2) ? argv[1] : "");
@@ -1704,10 +1704,23 @@
             error_exit("tcpip: invalid port: %s", argv[1]);
         }
         return adb_connect_command(android::base::StringPrintf("tcpip:%d", port));
+    } else if (!strcmp(argv[0], "remount")) {
+        FeatureSet features;
+        std::string error;
+        if (!adb_get_feature_set(&features, &error)) {
+            fprintf(stderr, "error: %s\n", error.c_str());
+            return 1;
+        }
+
+        if (CanUseFeature(features, kFeatureRemountShell)) {
+            const char* arg[2] = {"shell", "remount"};
+            return adb_shell(2, arg);
+        } else {
+            return adb_connect_command("remount:");
+        }
     }
     // clang-format off
-    else if (!strcmp(argv[0], "remount") ||
-             !strcmp(argv[0], "reboot") ||
+    else if (!strcmp(argv[0], "reboot") ||
              !strcmp(argv[0], "reboot-bootloader") ||
              !strcmp(argv[0], "reboot-fastboot") ||
              !strcmp(argv[0], "usb") ||
@@ -1739,41 +1752,33 @@
         // Determine the <host-prefix> for this command.
         std::string host_prefix;
         if (reverse) {
-            host_prefix = "reverse";
+            host_prefix = "reverse:";
         } else {
-            if (serial) {
-                host_prefix = android::base::StringPrintf("host-serial:%s", serial);
-            } else if (transport_type == kTransportUsb) {
-                host_prefix = "host-usb";
-            } else if (transport_type == kTransportLocal) {
-                host_prefix = "host-local";
-            } else {
-                host_prefix = "host";
-            }
+            host_prefix = "host:";
         }
 
         std::string cmd, error_message;
         if (strcmp(argv[0], "--list") == 0) {
             if (argc != 1) error_exit("--list doesn't take any arguments");
-            return adb_query_command(host_prefix + ":list-forward");
+            return adb_query_command(host_prefix + "list-forward");
         } else if (strcmp(argv[0], "--remove-all") == 0) {
             if (argc != 1) error_exit("--remove-all doesn't take any arguments");
-            cmd = host_prefix + ":killforward-all";
+            cmd = "killforward-all";
         } else if (strcmp(argv[0], "--remove") == 0) {
             // forward --remove <local>
             if (argc != 2) error_exit("--remove requires an argument");
-            cmd = host_prefix + ":killforward:" + argv[1];
+            cmd = std::string("killforward:") + argv[1];
         } else if (strcmp(argv[0], "--no-rebind") == 0) {
             // forward --no-rebind <local> <remote>
             if (argc != 3) error_exit("--no-rebind takes two arguments");
             if (forward_targets_are_valid(argv[1], argv[2], &error_message)) {
-                cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+                cmd = std::string("forward:norebind:") + argv[1] + ";" + argv[2];
             }
         } else {
             // forward <local> <remote>
             if (argc != 2) error_exit("forward takes two arguments");
             if (forward_targets_are_valid(argv[0], argv[1], &error_message)) {
-                cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
+                cmd = std::string("forward:") + argv[0] + ";" + argv[1];
             }
         }
 
@@ -1781,7 +1786,7 @@
             error_exit("error: %s", error_message.c_str());
         }
 
-        unique_fd fd(adb_connect(cmd, &error_message));
+        unique_fd fd(adb_connect(nullptr, host_prefix + cmd, &error_message, true));
         if (fd < 0 || !adb_status(fd.get(), &error_message)) {
             error_exit("error: %s", error_message.c_str());
         }
@@ -1843,8 +1848,8 @@
         }
 
         if (src.empty()) src = "all";
-        std::vector<std::string> partitions{"data",   "odm",   "oem", "product", "product_services",
-                                            "system", "vendor"};
+        std::vector<std::string> partitions{"data",   "odm",        "oem",   "product",
+                                            "system", "system_ext", "vendor"};
         bool found = false;
         for (const auto& partition : partitions) {
             if (src == "all" || src == partition) {
@@ -1892,7 +1897,10 @@
     } else if (!strcmp(argv[0], "track-jdwp")) {
         return adb_connect_command("track-jdwp");
     } else if (!strcmp(argv[0], "track-devices")) {
-        return adb_connect_command("host:track-devices");
+        if (argc > 2 || (argc == 2 && strcmp(argv[1], "-l"))) {
+            error_exit("usage: adb track-devices [-l]");
+        }
+        return adb_connect_command(argc == 2 ? "host:track-devices-l" : "host:track-devices");
     } else if (!strcmp(argv[0], "raw")) {
         if (argc != 2) {
             error_exit("usage: adb raw SERVICE");
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 6cfd4f7..cd5933a 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -17,7 +17,11 @@
 #ifndef COMMANDLINE_H
 #define COMMANDLINE_H
 
+#include <android-base/strings.h>
+
 #include "adb.h"
+#include "adb_client.h"
+#include "adb_unique_fd.h"
 
 // Callback used to handle the standard streams (stdout and stderr) sent by the
 // device's upon receiving a command.
@@ -105,4 +109,17 @@
         const std::string& command, bool disable_shell_protocol = false,
         StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
 
+// Connects to the device "abb" service with |command| and returns the fd.
+template <typename ContainerT>
+unique_fd send_abb_exec_command(const ContainerT& command_args, std::string* error) {
+    std::string service_string = "abb_exec:" + android::base::Join(command_args, ABB_ARG_DELIMETER);
+
+    unique_fd fd(adb_connect(service_string, error));
+    if (fd < 0) {
+        fprintf(stderr, "adb: failed to run abb_exec. Error: %s\n", error->c_str());
+        return unique_fd{};
+    }
+    return fd;
+}
+
 #endif  // COMMANDLINE_H
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index f4e8664..fbae219 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -27,6 +27,9 @@
 #include "androidfw/ZipFileRO.h"
 #include "client/file_sync_client.h"
 #include "commandline.h"
+#include "deployagent.inc"        // Generated include via build rule.
+#include "deployagentscript.inc"  // Generated include via build rule.
+#include "fastdeploy/deploypatchgenerator/deploy_patch_generator.h"
 #include "fastdeploycallbacks.h"
 #include "sysdeps.h"
 
@@ -35,6 +38,8 @@
 static constexpr long kRequiredAgentVersion = 0x00000002;
 
 static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+static constexpr const char* kDeviceAgentFile = "/data/local/tmp/deployagent.jar";
+static constexpr const char* kDeviceAgentScript = "/data/local/tmp/deployagent";
 
 static bool g_use_localagent = false;
 
@@ -71,46 +76,32 @@
     g_use_localagent = use_localagent;
 }
 
-// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
-static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
-    std::string adb_dir = android::base::GetExecutableDirectory();
-    if (adb_dir.empty()) {
-        error_exit("Could not determine location of adb!");
-    }
-
-    if (g_use_localagent) {
-        const char* product_out = getenv("ANDROID_PRODUCT_OUT");
-        if (product_out == nullptr) {
-            error_exit("Could not locate %s because $ANDROID_PRODUCT_OUT is not defined",
-                       local_path);
-        }
-        return android::base::StringPrintf("%s%s", product_out, local_path);
-    } else {
-        return adb_dir + sdk_path;
-    }
-}
-
 static bool deploy_agent(bool checkTimeStamps) {
     std::vector<const char*> srcs;
-    std::string jar_path =
-            get_agent_component_host_path("/system/framework/deployagent.jar", "/deployagent.jar");
-    std::string script_path =
-            get_agent_component_host_path("/system/bin/deployagent", "/deployagent");
-    srcs.push_back(jar_path.c_str());
-    srcs.push_back(script_path.c_str());
-
-    if (do_sync_push(srcs, kDeviceAgentPath, checkTimeStamps)) {
-        // on windows the shell script might have lost execute permission
-        // so need to set this explicitly
-        const char* kChmodCommandPattern = "chmod 777 %sdeployagent";
-        std::string chmodCommand =
-                android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
-        int ret = send_shell_command(chmodCommand);
-        if (ret != 0) {
-            error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
-        }
-    } else {
-        error_exit("Error pushing agent files to device");
+    // 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)) {
+        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();
+    // 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 =
+            android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentScript);
+    int ret = send_shell_command(chmodCommand);
+    if (ret != 0) {
+        error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
     }
 
     return true;
@@ -238,34 +229,15 @@
     }
 }
 
-static std::string get_patch_generator_command() {
-    if (g_use_localagent) {
-        // This should never happen on a Windows machine
-        const char* host_out = getenv("ANDROID_HOST_OUT");
-        if (host_out == nullptr) {
-            error_exit(
-                    "Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT "
-                    "is not defined");
-        }
-        return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
-                                           host_out);
-    }
-
-    std::string adb_dir = android::base::GetExecutableDirectory();
-    if (adb_dir.empty()) {
-        error_exit("Could not locate deploypatchgenerator.jar");
-    }
-    return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
-                                       adb_dir.c_str());
-}
-
 void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
-    std::string generatePatchCommand = android::base::StringPrintf(
-            R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
-            patchPath);
-    int returnCode = system(generatePatchCommand.c_str());
-    if (returnCode != 0) {
-        error_exit("Executing %s returned %d", generatePatchCommand.c_str(), returnCode);
+    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);
     }
 }
 
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 283fac5..1a34384 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -31,7 +31,7 @@
 
 #include "adb_mdns.h"
 #include "adb_trace.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
 #include "sysdeps.h"
 
 static DNSServiceRef service_ref;
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 81b8306..17b4db1 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -324,7 +324,7 @@
 
     h->urb_out_busy = true;
     while (true) {
-        auto now = std::chrono::system_clock::now();
+        auto now = std::chrono::steady_clock::now();
         if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
             // TODO: call USBDEVFS_DISCARDURB?
             errno = ETIMEDOUT;
diff --git a/adb/daemon/abb.cpp b/adb/daemon/abb.cpp
index 425438e..17c25e8 100644
--- a/adb/daemon/abb.cpp
+++ b/adb/daemon/abb.cpp
@@ -24,6 +24,7 @@
 #include "adb_io.h"
 #include "adb_utils.h"
 #include "shell_service.h"
+#include "sysdeps.h"
 
 namespace {
 
@@ -69,6 +70,11 @@
 }  // namespace
 
 static int execCmd(std::string_view args, borrowed_fd in, borrowed_fd out, borrowed_fd err) {
+    int max_buf = LINUX_MAX_SOCKET_SIZE;
+    adb_setsockopt(in, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
+    adb_setsockopt(out, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
+    adb_setsockopt(err, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
+
     AdbFdTextOutput oin(out);
     AdbFdTextOutput oerr(err);
     return cmdMain(parseCmdArgs(args), oin, oerr, in.get(), out.get(), err.get(),
@@ -98,6 +104,8 @@
         }
 
         unique_fd result = StartCommandInProcess(std::string(name), &execCmd, protocol);
+        int max_buf = LINUX_MAX_SOCKET_SIZE;
+        adb_setsockopt(result, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));
         if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {
             PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;
             break;
diff --git a/adb/daemon/abb_service.cpp b/adb/daemon/abb_service.cpp
index a435279..e1df4a5 100644
--- a/adb/daemon/abb_service.cpp
+++ b/adb/daemon/abb_service.cpp
@@ -53,14 +53,13 @@
             return error_fd;
         }
 
-        if (!SendProtocolString(socket_fd_, std::string(command))) {
+        if (!SendProtocolString(socket_fd_, command)) {
             PLOG(ERROR) << "failed to send command to abb";
             socket_fd_.reset();
             continue;
         }
 
         unique_fd fd;
-        std::string error;
         char buf;
         if (android::base::ReceiveFileDescriptors(socket_fd_, &buf, 1, &fd) != 1) {
             PLOG(ERROR) << "failed to receive FD from abb";
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 1800f84..2b8f461 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -18,7 +18,8 @@
 
 #include "adb.h"
 #include "adb_auth.h"
-#include "fdevent.h"
+#include "adb_io.h"
+#include "fdevent/fdevent.h"
 #include "sysdeps.h"
 #include "transport.h"
 
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 2a6418a..676f8e9 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -33,7 +33,6 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_utils.h"
-#include "fdevent.h"
 
 /* TODO:
 ** - sync with vsync to avoid tearing
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
index fca3c58..2204246 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -16,6 +16,8 @@
  * limitations under the License.
  */
 
+#include <linux/usb/functionfs.h>
+
 #include <atomic>
 #include <condition_variable>
 #include <mutex>
diff --git a/adb/daemon/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
index cd9b669..b92a7de 100644
--- a/adb/daemon/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -30,15 +30,21 @@
 
 #include <list>
 #include <memory>
+#include <thread>
 #include <vector>
 
+#include <adbconnection/server.h>
 #include <android-base/cmsg.h>
+#include <android-base/unique_fd.h>
 
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
 /* here's how these things work.
 
    when adbd starts, it creates a unix server socket
@@ -133,16 +139,16 @@
 static auto& _jdwp_list = *new std::list<std::unique_ptr<JdwpProcess>>();
 
 struct JdwpProcess {
-    explicit JdwpProcess(int socket) {
+    JdwpProcess(unique_fd socket, pid_t pid) {
+        CHECK(pid != 0);
+
         this->socket = socket;
-        this->fde = fdevent_create(socket, jdwp_process_event, this);
+        this->pid = pid;
+        this->fde = fdevent_create(socket.release(), jdwp_process_event, this);
 
         if (!this->fde) {
             LOG(FATAL) << "could not create fdevent for new JDWP process";
         }
-
-        /* start by waiting for the PID */
-        fdevent_add(this->fde, FDE_READ);
     }
 
     ~JdwpProcess() {
@@ -160,18 +166,12 @@
     }
 
     void RemoveFromList() {
-        if (this->pid >= 0) {
-            D("removing pid %d from jdwp process list", this->pid);
-        } else {
-            D("removing transient JdwpProcess from list");
-        }
-
         auto pred = [this](const auto& proc) { return proc.get() == this; };
         _jdwp_list.remove_if(pred);
     }
 
+    borrowed_fd socket = -1;
     int32_t pid = -1;
-    int socket = -1;
     fdevent* fde = nullptr;
 
     std::vector<unique_fd> out_fds;
@@ -181,11 +181,6 @@
     std::string temp;
 
     for (auto& proc : _jdwp_list) {
-        /* skip transient connections */
-        if (proc->pid < 0) {
-            continue;
-        }
-
         std::string next = std::to_string(proc->pid) + "\n";
         if (temp.length() + next.length() > bufferlen) {
             D("truncating JDWP process list (max len = %zu)", bufferlen);
@@ -214,24 +209,12 @@
 
 static void jdwp_process_event(int socket, unsigned events, void* _proc) {
     JdwpProcess* proc = reinterpret_cast<JdwpProcess*>(_proc);
-    CHECK_EQ(socket, proc->socket);
+    CHECK_EQ(socket, proc->socket.get());
 
     if (events & FDE_READ) {
-        if (proc->pid < 0) {
-            ssize_t rc = TEMP_FAILURE_RETRY(recv(socket, &proc->pid, sizeof(proc->pid), 0));
-            if (rc != sizeof(proc->pid)) {
-                D("failed to read jdwp pid: rc = %zd, errno = %s", rc, strerror(errno));
-                goto CloseProcess;
-            }
-
-            /* all is well, keep reading to detect connection closure */
-            D("Adding pid %d to jdwp process list", proc->pid);
-            jdwp_process_list_updated();
-        } else {
-            // We already have the PID, if we can read from the socket, we've probably hit EOF.
-            D("terminating JDWP connection %d", proc->pid);
-            goto CloseProcess;
-        }
+        // We already have the PID, if we can read from the socket, we've probably hit EOF.
+        D("terminating JDWP connection %d", proc->pid);
+        goto CloseProcess;
     }
 
     if (events & FDE_WRITE) {
@@ -284,98 +267,6 @@
     return unique_fd{};
 }
 
-/**  VM DEBUG CONTROL SOCKET
- **
- **  we do implement a custom asocket to receive the data
- **/
-
-/* name of the debug control Unix socket */
-#define JDWP_CONTROL_NAME "\0jdwp-control"
-#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1)
-
-struct JdwpControl {
-    int listen_socket;
-    fdevent* fde;
-};
-
-static JdwpControl _jdwp_control;
-
-static void jdwp_control_event(int s, unsigned events, void* user);
-
-static int jdwp_control_init(JdwpControl* control, const char* sockname, int socknamelen) {
-    sockaddr_un addr;
-    socklen_t addrlen;
-    int maxpath = sizeof(addr.sun_path);
-    int pathlen = socknamelen;
-
-    if (pathlen >= maxpath) {
-        D("vm debug control socket name too long (%d extra chars)", pathlen + 1 - maxpath);
-        return -1;
-    }
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sun_family = AF_UNIX;
-    memcpy(addr.sun_path, sockname, socknamelen);
-
-    unique_fd s(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0));
-    if (s < 0) {
-        D("could not create vm debug control socket. %d: %s", errno, strerror(errno));
-        return -1;
-    }
-
-    addrlen = pathlen + sizeof(addr.sun_family);
-
-    if (bind(s.get(), reinterpret_cast<sockaddr*>(&addr), addrlen) < 0) {
-        D("could not bind vm debug control socket: %d: %s", errno, strerror(errno));
-        return -1;
-    }
-
-    if (listen(s.get(), 4) < 0) {
-        D("listen failed in jdwp control socket: %d: %s", errno, strerror(errno));
-        return -1;
-    }
-
-    control->listen_socket = s.release();
-    control->fde = fdevent_create(control->listen_socket, jdwp_control_event, control);
-    if (control->fde == nullptr) {
-        D("could not create fdevent for jdwp control socket");
-        return -1;
-    }
-
-    /* only wait for incoming connections */
-    fdevent_add(control->fde, FDE_READ);
-
-    D("jdwp control socket started (%d)", control->listen_socket);
-    return 0;
-}
-
-static void jdwp_control_event(int fd, unsigned events, void* _control) {
-    JdwpControl* control = (JdwpControl*)_control;
-
-    CHECK_EQ(fd, control->listen_socket);
-    if (events & FDE_READ) {
-        int s = adb_socket_accept(control->listen_socket, nullptr, nullptr);
-        if (s < 0) {
-            if (errno == ECONNABORTED) {
-                /* oops, the JDWP process died really quick */
-                D("oops, the JDWP process died really quick");
-                return;
-            } else {
-                /* the socket is probably closed ? */
-                D("weird accept() failed on jdwp control socket: %s", strerror(errno));
-                return;
-            }
-        }
-
-        auto proc = std::make_unique<JdwpProcess>(s);
-        if (!proc) {
-            LOG(FATAL) << "failed to allocate JdwpProcess";
-        }
-
-        _jdwp_list.emplace_back(std::move(proc));
-    }
-}
-
 /** "jdwp" local service implementation
  ** this simply returns the list of known JDWP process pids
  **/
@@ -526,7 +417,22 @@
 }
 
 int init_jdwp(void) {
-    return jdwp_control_init(&_jdwp_control, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
+    std::thread([]() {
+        adb_thread_setname("jdwp control");
+        adbconnection_listen([](int fd, pid_t pid) {
+            LOG(INFO) << "jdwp connection from " << pid;
+            fdevent_run_on_main_thread([fd, pid] {
+                unique_fd ufd(fd);
+                auto proc = std::make_unique<JdwpProcess>(std::move(ufd), pid);
+                if (!proc) {
+                    LOG(FATAL) << "failed to allocate JdwpProcess";
+                }
+                _jdwp_list.emplace_back(std::move(proc));
+                jdwp_process_list_updated();
+            });
+        });
+    }).detach();
+    return 0;
 }
 
 #endif /* !ADB_HOST */
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index ce494ee..6bd7855 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -48,6 +48,8 @@
         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);
     }
 
@@ -83,6 +85,6 @@
 }
 
 void remount_service(unique_fd fd, const std::string& cmd) {
-    const char* success = do_remount(fd.get(), cmd) ? "succeeded" : "failed";
-    WriteFdFmt(fd.get(), "remount %s\n", success);
+    do_remount(fd.get(), cmd);
+    // The remount command will print success or failure for us.
 }
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index de97068..0fb14c4 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -222,7 +222,7 @@
 bool Subprocess::ForkAndExec(std::string* error) {
     unique_fd child_stdinout_sfd, child_stderr_sfd;
     unique_fd parent_error_sfd, child_error_sfd;
-    char pts_name[PATH_MAX];
+    const char* pts_name = nullptr;
 
     if (command_.empty()) {
         __android_log_security_bswrite(SEC_TAG_ADB_SHELL_INTERACTIVE, "");
@@ -283,10 +283,22 @@
     cenv.push_back(nullptr);
 
     if (type_ == SubprocessType::kPty) {
-        int fd;
-        pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
+        unique_fd pty_master(posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC));
+        if (pty_master == -1) {
+            *error =
+                    android::base::StringPrintf("failed to create pty master: %s", strerror(errno));
+            return false;
+        }
+        if (unlockpt(pty_master.get()) != 0) {
+            *error = android::base::StringPrintf("failed to unlockpt pty master: %s",
+                                                 strerror(errno));
+            return false;
+        }
+
+        pid_ = fork();
+        pts_name = ptsname(pty_master.get());
         if (pid_ > 0) {
-          stdinout_sfd_.reset(fd);
+            stdinout_sfd_ = std::move(pty_master);
         }
     } else {
         if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp
index a64ce40..338d776 100644
--- a/adb/daemon/usb_ffs.cpp
+++ b/adb/daemon/usb_ffs.cpp
@@ -299,7 +299,6 @@
         }
         // Signal only when writing the descriptors to ffs
         android::base::SetProperty("sys.usb.ffs.ready", "1");
-        *out_control = std::move(control);
     }
 
     bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
@@ -314,6 +313,7 @@
         return false;
     }
 
+    *out_control = std::move(control);
     *out_bulk_in = std::move(bulk_in);
     *out_bulk_out = std::move(bulk_out);
     return true;
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 1ba0de0..95e1a28 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -13,27 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-
 java_binary {
     name: "deployagent",
     sdk_version: "24",
     srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
     static_libs: ["apkzlib_zip"],
-    wrapper: "deployagent/deployagent.sh",
     proto: {
         type: "lite",
     },
     dex_preopt: {
         enabled: false,
     }
-}
-
-java_binary_host {
-    name: "deploypatchgenerator",
-    srcs: ["deploypatchgenerator/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
-    static_libs: ["apkzlib"],
-    manifest: "deploypatchgenerator/manifest.txt",
-    proto: {
-        type: "full",
-    }
-}
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
index 2d3b135..a8103c4 100644
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -181,7 +181,7 @@
     private static void extractMetaData(String packageName) throws IOException {
         File apkFile = getFileFromPackageName(packageName);
         APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
-        apkMetaData.writeDelimitedTo(System.out);
+        apkMetaData.writeTo(System.out);
     }
 
     private static int createInstallSession(String[] args) throws IOException {
@@ -223,7 +223,6 @@
         }
 
         int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
-
         if (writeExitCode == 0) {
             return commitInstallSession(sessionId);
         } else {
@@ -285,7 +284,6 @@
             if (oldDataLen > 0) {
                 PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
             }
-
             newDataBytesWritten += copyLen + oldDataLen;
         }
 
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
index f0f00e1..c60f9a6 100644
--- a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
@@ -39,7 +39,7 @@
 class PatchUtils {
     private static final long NEGATIVE_MASK = 1L << 63;
     private static final long NEGATIVE_LONG_SIGN_MASK = 1L << 63;
-    public static final String SIGNATURE = "HAMADI/IHD";
+    public static final String SIGNATURE = "FASTDEPLOY";
 
     private static long getOffsetFromEntry(StoredEntry entry) {
         return entry.getCentralDirectoryHeader().getOffset() + entry.getLocalHeaderSize();
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
new file mode 100644
index 0000000..22c9243
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "deploy_patch_generator.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <algorithm>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "adb_unique_fd.h"
+#include "android-base/file.h"
+#include "patch_utils.h"
+#include "sysdeps.h"
+
+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);
+    printf("\n");
+    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());
+}
+
+void DeployPatchGenerator::APKMetaDataToLog(const char* file, const APKMetaData& metadata) {
+    if (!is_verbose_) {
+        return;
+    }
+    Log("APK Metadata: %s", file);
+    for (int i = 0; i < metadata.entries_size(); i++) {
+        const APKEntry& entry = metadata.entries(i);
+        APKEntryToLog(entry);
+    }
+}
+
+void DeployPatchGenerator::ReportSavings(const std::vector<SimpleEntry>& identicalEntries,
+                                         uint64_t totalSize) {
+    long totalEqualBytes = 0;
+    int totalEqualFiles = 0;
+    for (size_t i = 0; i < identicalEntries.size(); i++) {
+        if (identicalEntries[i].deviceEntry != nullptr) {
+            totalEqualBytes += identicalEntries[i].localEntry->compressedsize();
+            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);
+}
+
+void DeployPatchGenerator::GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
+                                         const char* localApkPath, borrowed_fd output) {
+    unique_fd input(adb_open(localApkPath, O_RDONLY | O_CLOEXEC));
+    size_t newApkSize = adb_lseek(input, 0L, SEEK_END);
+    adb_lseek(input, 0L, SEEK_SET);
+
+    PatchUtils::WriteSignature(output);
+    PatchUtils::WriteLong(newApkSize, output);
+    size_t currentSizeOut = 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();
+        int64_t hostDataOffset = entry.localEntry->dataoffset();
+        int64_t deviceDataLength = entry.deviceEntry->compressedsize();
+        int64_t deltaFromDeviceDataStart = hostDataOffset - currentSizeOut;
+        PatchUtils::WriteLong(deltaFromDeviceDataStart, output);
+        if (deltaFromDeviceDataStart > 0) {
+            PatchUtils::Pipe(input, output, deltaFromDeviceDataStart);
+        }
+        PatchUtils::WriteLong(deviceDataOffset, output);
+        PatchUtils::WriteLong(deviceDataLength, output);
+        adb_lseek(input, deviceDataLength, SEEK_CUR);
+        currentSizeOut += deltaFromDeviceDataStart + deviceDataLength;
+    }
+    if (currentSizeOut != newApkSize) {
+        PatchUtils::WriteLong(newApkSize - currentSizeOut, output);
+        PatchUtils::Pipe(input, output, newApkSize - currentSizeOut);
+        PatchUtils::WriteLong(0, output);
+        PatchUtils::WriteLong(0, output);
+    }
+}
+
+bool DeployPatchGenerator::CreatePatch(const char* localApkPath, 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|.
+    }
+
+    APKMetaData localApkMetadata = PatchUtils::GetAPKMetaData(localApkPath);
+    // Log gathered metadata info.
+    APKMetaDataToLog(deviceApkMetadataPath, deviceApkMetadata);
+    APKMetaDataToLog(localApkPath, localApkMetadata);
+
+    std::vector<SimpleEntry> identicalEntries;
+    uint64_t totalSize =
+            BuildIdenticalEntries(identicalEntries, localApkMetadata, deviceApkMetadata);
+    ReportSavings(identicalEntries, totalSize);
+    GeneratePatch(identicalEntries, localApkPath, output);
+    return true;
+}
+
+uint64_t DeployPatchGenerator::BuildIdenticalEntries(std::vector<SimpleEntry>& outIdenticalEntries,
+                                                     const APKMetaData& localApkMetadata,
+                                                     const APKMetaData& deviceApkMetadata) {
+    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) {
+                SimpleEntry simpleEntry;
+                simpleEntry.localEntry = const_cast<APKEntry*>(&localEntry);
+                simpleEntry.deviceEntry = const_cast<APKEntry*>(&deviceEntry);
+                APKEntryToLog(localEntry);
+                outIdenticalEntries.push_back(simpleEntry);
+                break;
+            }
+        }
+    }
+    std::sort(outIdenticalEntries.begin(), outIdenticalEntries.end(),
+              [](const SimpleEntry& lhs, const SimpleEntry& rhs) {
+                  return lhs.localEntry->dataoffset() < rhs.localEntry->dataoffset();
+              });
+    return totalSize;
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
new file mode 100644
index 0000000..30e41a5
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "adb_unique_fd.h"
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+/**
+ * This class is responsible for creating a patch that can be accepted by the deployagent. The
+ * patch format is documented in GeneratePatch.
+ */
+class DeployPatchGenerator {
+  public:
+    /**
+     * Simple struct to hold mapping between local metadata and device metadata.
+     */
+    struct SimpleEntry {
+        com::android::fastdeploy::APKEntry* localEntry;
+        com::android::fastdeploy::APKEntry* deviceEntry;
+    };
+
+    /**
+     * If |is_verbose| is true ApkEntries that are similar between device and host are written to
+     * the console.
+     */
+    explicit DeployPatchGenerator(bool is_verbose) : is_verbose_(is_verbose) {}
+    /**
+     * Given a |localApkPath|, and the |deviceApkMetadataPath| from an installed APK this function
+     * writes a patch to the given |output|.
+     */
+    bool CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
+                     android::base::borrowed_fd output);
+
+  private:
+    bool is_verbose_;
+
+    /**
+     * Log function only logs data to stdout when |is_verbose_| is true.
+     */
+    void Log(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
+
+    /**
+     * Helper function to log the APKMetaData structure. If |is_verbose_| is false this function
+     * early outs. |file| is the path to the file represented by |metadata|. This function is used
+     * for debugging / information.
+     */
+    void APKMetaDataToLog(const char* file, const com::android::fastdeploy::APKMetaData& metadata);
+    /**
+     * Helper function to log APKEntry.
+     */
+    void APKEntryToLog(const com::android::fastdeploy::APKEntry& entry);
+
+    /**
+     * Helper function to report savings by fastdeploy. This function prints out savings even with
+     * |is_verbose_| set to false. |totalSize| is used to show a percentage of savings. Note:
+     * |totalSize| is the size of the ZipEntries. Not the size of the entire file. The metadata of
+     * the zip data needs to be sent across with every iteration.
+     * [Patch format]
+     * |Fixed String| Signature
+     * |long|         New Size of Apk
+     * |Packets[]|    Array of Packets
+     *
+     * [Packet Format]
+     * |long|     Size of data to use from patch
+     * |byte[]|   Patch data
+     * |long|     Offset of data to use already on device
+     * |long|     Length of data to read from device APK
+     * TODO(b/138306784): Move the patch format to a proto.
+     */
+    void ReportSavings(const std::vector<SimpleEntry>& identicalEntries, uint64_t totalSize);
+
+    /**
+     * This enumerates each entry in |entriesToUseOnDevice| and builds a patch file copying data
+     * from |localApkPath| where we are unable to use entries already on the device. The new patch
+     * is written to |output|. The entries are expected to be sorted by data offset from lowest to
+     * highest.
+     */
+    void GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
+                       const char* localApkPath, 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
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
new file mode 100644
index 0000000..9cdc44e
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "deploy_patch_generator.h"
+#include "patch_utils.h"
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+
+#include "sysdeps.h"
+
+using namespace com::android::fastdeploy;
+
+static std::string GetTestFile(const std::string& name) {
+    return "fastdeploy/testdata/" + name;
+}
+
+class TestPatchGenerator : DeployPatchGenerator {
+  public:
+    TestPatchGenerator() : DeployPatchGenerator(false) {}
+    void GatherIdenticalEntries(std::vector<DeployPatchGenerator::SimpleEntry>& outIdenticalEntries,
+                                const APKMetaData& metadataA, const APKMetaData& metadataB) {
+        BuildIdenticalEntries(outIdenticalEntries, metadataA, metadataB);
+    }
+};
+
+TEST(DeployPatchGeneratorTest, IdenticalFileEntries) {
+    std::string apkPath = GetTestFile("rotating_cube-release.apk");
+    APKMetaData metadataA = PatchUtils::GetAPKMetaData(apkPath.c_str());
+    TestPatchGenerator generator;
+    std::vector<DeployPatchGenerator::SimpleEntry> entries;
+    generator.GatherIdenticalEntries(entries, metadataA, metadataA);
+    // Expect the entry count to match the number of entries in the metadata.
+    const uint32_t identicalCount = entries.size();
+    const uint32_t entriesCount = metadataA.entries_size();
+    EXPECT_EQ(identicalCount, entriesCount);
+}
+
+TEST(DeployPatchGeneratorTest, NoDeviceMetadata) {
+    std::string apkPath = GetTestFile("rotating_cube-release.apk");
+    // Get size of our test apk.
+    long apkSize = 0;
+    {
+        unique_fd apkFile(adb_open(apkPath.c_str(), O_RDWR));
+        apkSize = adb_lseek(apkFile, 0L, SEEK_END);
+    }
+
+    // Create a patch that is 100% different.
+    TemporaryFile output;
+    DeployPatchGenerator generator(true);
+    generator.CreatePatch(apkPath.c_str(), "", output.fd);
+
+    // Expect a patch file that has a size at least the size of our initial APK.
+    long patchSize = adb_lseek(output.fd, 0L, SEEK_END);
+    EXPECT_GT(patchSize, apkSize);
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/manifest.txt b/adb/fastdeploy/deploypatchgenerator/manifest.txt
deleted file mode 100644
index 5c00505..0000000
--- a/adb/fastdeploy/deploypatchgenerator/manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.fastdeploy.DeployPatchGenerator
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
new file mode 100644
index 0000000..f11ddd1
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "patch_utils.h"
+
+#include <androidfw/ZipFileRO.h>
+#include <stdio.h>
+
+#include "adb_io.h"
+#include "android-base/endian.h"
+#include "sysdeps.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 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;
+            }
+
+            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);
+        }
+    }
+    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));
+}
+
+void PatchUtils::Pipe(borrowed_fd input, borrowed_fd output, size_t amount) {
+    constexpr static int 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);
+        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
new file mode 100644
index 0000000..0ebfe8f
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "adb_unique_fd.h"
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+/**
+ * Helper class that mirrors the PatchUtils from deploy agent.
+ */
+class PatchUtils {
+  public:
+    /**
+     * This function takes 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);
+    /**
+     * Writes a fixed signature string to the header of the patch.
+     */
+    static void WriteSignature(android::base::borrowed_fd output);
+    /**
+     * Writes an int64 to the |output| reversing the bytes.
+     */
+    static void WriteLong(int64_t value, android::base::borrowed_fd output);
+    /**
+     * 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
new file mode 100644
index 0000000..a7eeebf
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "patch_utils.h"
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sstream>
+#include <string>
+
+#include "adb_io.h"
+#include "sysdeps.h"
+
+using namespace com::android::fastdeploy;
+
+static std::string GetTestFile(const std::string& name) {
+    return "fastdeploy/testdata/" + name;
+}
+
+bool FileMatchesContent(android::base::borrowed_fd input, const char* contents,
+                        ssize_t contentsSize) {
+    adb_lseek(input, 0, SEEK_SET);
+    // Use a temp buffer larger than any test contents.
+    constexpr int BUFFER_SIZE = 2048;
+    char buffer[BUFFER_SIZE];
+    bool result = true;
+    // Validate size of files is equal.
+    ssize_t readAmount = adb_read(input, buffer, BUFFER_SIZE);
+    EXPECT_EQ(readAmount, contentsSize);
+    result = memcmp(buffer, contents, readAmount) == 0;
+    for (int i = 0; i < readAmount; i++) {
+        printf("%x", buffer[i]);
+    }
+    printf(" == ");
+    for (int i = 0; i < contentsSize; i++) {
+        printf("%x", contents[i]);
+    }
+    printf("\n");
+
+    return result;
+}
+
+TEST(PatchUtilsTest, SwapLongWrites) {
+    TemporaryFile output;
+    PatchUtils::WriteLong(0x0011223344556677, output.fd);
+    adb_lseek(output.fd, 0, SEEK_SET);
+    const char expected[] = {0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
+    EXPECT_TRUE(FileMatchesContent(output.fd, expected, 8));
+}
+
+TEST(PatchUtilsTest, PipeWritesAmountToOutput) {
+    std::string expected("Some Data");
+    TemporaryFile input;
+    TemporaryFile output;
+    // Populate input file.
+    WriteFdExactly(input.fd, expected);
+    adb_lseek(input.fd, 0, SEEK_SET);
+    // Open input file for read, and output file for write.
+    PatchUtils::Pipe(input.fd, output.fd, expected.size());
+    // Validate pipe worked
+    EXPECT_TRUE(FileMatchesContent(output.fd, expected.c_str(), expected.size()));
+}
+
+TEST(PatchUtilsTest, SignatureConstMatches) {
+    std::string apkFile = GetTestFile("rotating_cube-release.apk");
+    TemporaryFile output;
+    PatchUtils::WriteSignature(output.fd);
+    std::string contents("FASTDEPLOY");
+    EXPECT_TRUE(FileMatchesContent(output.fd, contents.c_str(), contents.size()));
+}
+
+TEST(PatchUtilsTest, GatherMetadata) {
+    std::string apkFile = GetTestFile("rotating_cube-release.apk");
+    APKMetaData metadata = PatchUtils::GetAPKMetaData(apkFile.c_str());
+    std::string expectedMetadata;
+    android::base::ReadFileToString(GetTestFile("rotating_cube-metadata-release.data"),
+                                    &expectedMetadata);
+    std::string actualMetadata;
+    metadata.SerializeToString(&actualMetadata);
+    EXPECT_EQ(expectedMetadata, actualMetadata);
+}
\ No newline at end of file
diff --git a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
deleted file mode 100644
index 24b2eab..0000000
--- a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
+++ /dev/null
@@ -1,208 +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.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.StringBuilder;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.ArrayList;
-
-import java.nio.charset.StandardCharsets;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.AbstractMap.SimpleEntry;
-
-import com.android.fastdeploy.APKMetaData;
-import com.android.fastdeploy.APKEntry;
-
-public final class DeployPatchGenerator {
-    private static final int BUFFER_SIZE = 128 * 1024;
-
-    public static void main(String[] args) {
-        try {
-            if (args.length < 2) {
-                showUsage(0);
-            }
-
-            boolean verbose = false;
-            if (args.length > 2) {
-                String verboseFlag = args[2];
-                if (verboseFlag.compareTo("--verbose") == 0) {
-                    verbose = true;
-                }
-            }
-
-            StringBuilder sb = null;
-            String apkPath = args[0];
-            String deviceMetadataPath = args[1];
-            File hostFile = new File(apkPath);
-
-            List<APKEntry> deviceZipEntries = getMetadataFromFile(deviceMetadataPath);
-            System.err.println("Device Entries (" + deviceZipEntries.size() + ")");
-            if (verbose) {
-                sb = new StringBuilder();
-                for (APKEntry entry : deviceZipEntries) {
-                    APKEntryToString(entry, sb);
-                }
-                System.err.println(sb.toString());
-            }
-
-            List<APKEntry> hostFileEntries = PatchUtils.getAPKMetaData(hostFile).getEntriesList();
-            System.err.println("Host Entries (" + hostFileEntries.size() + ")");
-            if (verbose) {
-                sb = new StringBuilder();
-                for (APKEntry entry : hostFileEntries) {
-                    APKEntryToString(entry, sb);
-                }
-                System.err.println(sb.toString());
-            }
-
-            List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet =
-                getIdenticalContents(deviceZipEntries, hostFileEntries);
-            reportIdenticalContents(identicalContentsEntrySet, hostFile);
-
-            if (verbose) {
-                sb = new StringBuilder();
-                for (SimpleEntry<APKEntry, APKEntry> identicalEntry : identicalContentsEntrySet) {
-                    APKEntry entry = identicalEntry.getValue();
-                    APKEntryToString(entry, sb);
-                }
-                System.err.println("Identical Entries (" + identicalContentsEntrySet.size() + ")");
-                System.err.println(sb.toString());
-            }
-
-            createPatch(identicalContentsEntrySet, hostFile, System.out);
-        } catch (Exception e) {
-            System.err.println("Error: " + e);
-            e.printStackTrace();
-            System.exit(2);
-        }
-        System.exit(0);
-    }
-
-    private static void showUsage(int exitCode) {
-        System.err.println("usage: deploypatchgenerator <apkpath> <deviceapkmetadata> [--verbose]");
-        System.err.println("");
-        System.exit(exitCode);
-    }
-
-    private static void APKEntryToString(APKEntry entry, StringBuilder outputString) {
-        outputString.append(String.format("Filename: %s\n", entry.getFileName()));
-        outputString.append(String.format("CRC32: 0x%08X\n", entry.getCrc32()));
-        outputString.append(String.format("Data Offset: %d\n", entry.getDataOffset()));
-        outputString.append(String.format("Compressed Size: %d\n", entry.getCompressedSize()));
-        outputString.append(String.format("Uncompressed Size: %d\n", entry.getUncompressedSize()));
-    }
-
-    private static List<APKEntry> getMetadataFromFile(String deviceMetadataPath) throws IOException {
-        InputStream is = new FileInputStream(new File(deviceMetadataPath));
-        APKMetaData apkMetaData = APKMetaData.parseDelimitedFrom(is);
-        return apkMetaData.getEntriesList();
-    }
-
-    private static List<SimpleEntry<APKEntry, APKEntry>> getIdenticalContents(
-        List<APKEntry> deviceZipEntries, List<APKEntry> hostZipEntries) throws IOException {
-        List<SimpleEntry<APKEntry, APKEntry>> identicalContents =
-            new ArrayList<SimpleEntry<APKEntry, APKEntry>>();
-
-        for (APKEntry deviceZipEntry : deviceZipEntries) {
-            for (APKEntry hostZipEntry : hostZipEntries) {
-                if (deviceZipEntry.getCrc32() == hostZipEntry.getCrc32() &&
-                    deviceZipEntry.getFileName().equals(hostZipEntry.getFileName())) {
-                    identicalContents.add(new SimpleEntry(deviceZipEntry, hostZipEntry));
-                }
-            }
-        }
-
-        Collections.sort(identicalContents, new Comparator<SimpleEntry<APKEntry, APKEntry>>() {
-            @Override
-            public int compare(
-                SimpleEntry<APKEntry, APKEntry> p1, SimpleEntry<APKEntry, APKEntry> p2) {
-                return Long.compare(p1.getValue().getDataOffset(), p2.getValue().getDataOffset());
-            }
-        });
-
-        return identicalContents;
-    }
-
-    private static void reportIdenticalContents(
-        List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet, File hostFile)
-        throws IOException {
-        long totalEqualBytes = 0;
-        int totalEqualFiles = 0;
-        for (SimpleEntry<APKEntry, APKEntry> entries : identicalContentsEntrySet) {
-            APKEntry hostAPKEntry = entries.getValue();
-            totalEqualBytes += hostAPKEntry.getCompressedSize();
-            totalEqualFiles++;
-        }
-
-        float savingPercent = (float) (totalEqualBytes * 100) / hostFile.length();
-
-        System.err.println("Detected " + totalEqualFiles + " equal APK entries");
-        System.err.println(totalEqualBytes + " bytes are equal out of " + hostFile.length() + " ("
-            + savingPercent + "%)");
-    }
-
-    static void createPatch(List<SimpleEntry<APKEntry, APKEntry>> zipEntrySimpleEntrys,
-        File hostFile, OutputStream patchStream) throws IOException, PatchFormatException {
-        FileInputStream hostFileInputStream = new FileInputStream(hostFile);
-
-        patchStream.write(PatchUtils.SIGNATURE.getBytes(StandardCharsets.US_ASCII));
-        PatchUtils.writeFormattedLong(hostFile.length(), patchStream);
-
-        byte[] buffer = new byte[BUFFER_SIZE];
-        long totalBytesWritten = 0;
-        Iterator<SimpleEntry<APKEntry, APKEntry>> entrySimpleEntryIterator =
-            zipEntrySimpleEntrys.iterator();
-        while (entrySimpleEntryIterator.hasNext()) {
-            SimpleEntry<APKEntry, APKEntry> entrySimpleEntry = entrySimpleEntryIterator.next();
-            APKEntry deviceAPKEntry = entrySimpleEntry.getKey();
-            APKEntry hostAPKEntry = entrySimpleEntry.getValue();
-
-            long newDataLen = hostAPKEntry.getDataOffset() - totalBytesWritten;
-            long oldDataOffset = deviceAPKEntry.getDataOffset();
-            long oldDataLen = deviceAPKEntry.getCompressedSize();
-
-            PatchUtils.writeFormattedLong(newDataLen, patchStream);
-            PatchUtils.pipe(hostFileInputStream, patchStream, buffer, newDataLen);
-            PatchUtils.writeFormattedLong(oldDataOffset, patchStream);
-            PatchUtils.writeFormattedLong(oldDataLen, patchStream);
-
-            long skip = hostFileInputStream.skip(oldDataLen);
-            if (skip != oldDataLen) {
-                throw new PatchFormatException("skip error: attempted to skip " + oldDataLen
-                    + " bytes but return code was " + skip);
-            }
-            totalBytesWritten += oldDataLen + newDataLen;
-        }
-        long remainderLen = hostFile.length() - totalBytesWritten;
-        PatchUtils.writeFormattedLong(remainderLen, patchStream);
-        PatchUtils.pipe(hostFileInputStream, patchStream, buffer, remainderLen);
-        PatchUtils.writeFormattedLong(0, patchStream);
-        PatchUtils.writeFormattedLong(0, patchStream);
-        patchStream.flush();
-    }
-}
diff --git a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
new file mode 100644
index 0000000..0671bf3
--- /dev/null
+++ b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
@@ -0,0 +1,6 @@
+
+#ǂϫMETA-INF/MANIFEST.MFÇ Q(W
+#ƒ•ŽAndroidManifest.xml1 ä(è
+6¦µ€>#lib/armeabi-v7a/libvulkan_sample.so ÀÒQ(Œ²ì
+ —ã—‘resources.arscôàQ ´(´
+‹œÂÉclasses.dexÁ ÿ(ô
diff --git a/adb/fastdeploy/testdata/rotating_cube-release.apk b/adb/fastdeploy/testdata/rotating_cube-release.apk
new file mode 100644
index 0000000..d47e0ea
--- /dev/null
+++ b/adb/fastdeploy/testdata/rotating_cube-release.apk
Binary files differ
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
deleted file mode 100644
index 32f9086..0000000
--- a/adb/fdevent.cpp
+++ /dev/null
@@ -1,553 +0,0 @@
-/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
-**
-** Copyright 2006, Brian Swetland <swetland@frotz.net>
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define TRACE_TAG FDEVENT
-
-#include "sysdeps.h"
-#include "fdevent.h"
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <atomic>
-#include <deque>
-#include <functional>
-#include <list>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-#include <utility>
-#include <variant>
-#include <vector>
-
-#include <android-base/chrono_utils.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/thread_annotations.h>
-#include <android-base/threads.h>
-
-#include "adb_io.h"
-#include "adb_trace.h"
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "sysdeps/chrono.h"
-
-#define FDE_EVENTMASK  0x00ff
-#define FDE_STATEMASK  0xff00
-
-#define FDE_ACTIVE     0x0100
-#define FDE_PENDING    0x0200
-#define FDE_CREATED    0x0400
-
-struct PollNode {
-  fdevent* fde;
-  adb_pollfd pollfd;
-
-  explicit PollNode(fdevent* fde) : fde(fde) {
-      memset(&pollfd, 0, sizeof(pollfd));
-      pollfd.fd = fde->fd.get();
-
-#if defined(__linux__)
-      // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
-      // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
-      pollfd.events = POLLRDHUP;
-#endif
-  }
-};
-
-// All operations to fdevent should happen only in the main thread.
-// That's why we don't need a lock for fdevent.
-static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
-static auto& g_pending_list = *new std::list<fdevent*>();
-static std::atomic<bool> terminate_loop(false);
-static bool main_thread_valid;
-static uint64_t main_thread_id;
-
-static uint64_t fdevent_id;
-
-static bool run_needs_flush = false;
-static auto& run_queue_notify_fd = *new unique_fd();
-static auto& run_queue_mutex = *new std::mutex();
-static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::deque<std::function<void()>>();
-
-void check_main_thread() {
-    if (main_thread_valid) {
-        CHECK_EQ(main_thread_id, android::base::GetThreadId());
-    }
-}
-
-void set_main_thread() {
-    main_thread_valid = true;
-    main_thread_id = android::base::GetThreadId();
-}
-
-static std::string dump_fde(const fdevent* fde) {
-    std::string state;
-    if (fde->state & FDE_ACTIVE) {
-        state += "A";
-    }
-    if (fde->state & FDE_PENDING) {
-        state += "P";
-    }
-    if (fde->state & FDE_CREATED) {
-        state += "C";
-    }
-    if (fde->state & FDE_READ) {
-        state += "R";
-    }
-    if (fde->state & FDE_WRITE) {
-        state += "W";
-    }
-    if (fde->state & FDE_ERROR) {
-        state += "E";
-    }
-    return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
-                                       state.c_str());
-}
-
-template <typename F>
-static fdevent* fdevent_create_impl(int fd, F func, void* arg) {
-    check_main_thread();
-    CHECK_GE(fd, 0);
-
-    fdevent* fde = new fdevent();
-    fde->id = fdevent_id++;
-    fde->state = FDE_ACTIVE;
-    fde->fd.reset(fd);
-    fde->func = func;
-    fde->arg = arg;
-    if (!set_file_block_mode(fd, false)) {
-        // Here is not proper to handle the error. If it fails here, some error is
-        // likely to be detected by poll(), then we can let the callback function
-        // to handle it.
-        LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
-    }
-    auto pair = g_poll_node_map.emplace(fde->fd.get(), PollNode(fde));
-    CHECK(pair.second) << "install existing fd " << fd;
-
-    fde->state |= FDE_CREATED;
-    return fde;
-}
-
-fdevent* fdevent_create(int fd, fd_func func, void* arg) {
-    return fdevent_create_impl(fd, func, arg);
-}
-
-fdevent* fdevent_create(int fd, fd_func2 func, void* arg) {
-    return fdevent_create_impl(fd, func, arg);
-}
-
-unique_fd fdevent_release(fdevent* fde) {
-    check_main_thread();
-    if (!fde) {
-        return {};
-    }
-
-    if (!(fde->state & FDE_CREATED)) {
-        LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
-    }
-
-    unique_fd result = std::move(fde->fd);
-    if (fde->state & FDE_ACTIVE) {
-        g_poll_node_map.erase(result.get());
-
-        if (fde->state & FDE_PENDING) {
-            g_pending_list.remove(fde);
-        }
-        fde->state = 0;
-        fde->events = 0;
-    }
-
-    delete fde;
-    return result;
-}
-
-void fdevent_destroy(fdevent* fde) {
-    // Release, and then let unique_fd's destructor cleanup.
-    fdevent_release(fde);
-}
-
-static void fdevent_update(fdevent* fde, unsigned events) {
-    auto it = g_poll_node_map.find(fde->fd.get());
-    CHECK(it != g_poll_node_map.end());
-    PollNode& node = it->second;
-    if (events & FDE_READ) {
-        node.pollfd.events |= POLLIN;
-    } else {
-        node.pollfd.events &= ~POLLIN;
-    }
-
-    if (events & FDE_WRITE) {
-        node.pollfd.events |= POLLOUT;
-    } else {
-        node.pollfd.events &= ~POLLOUT;
-    }
-    fde->state = (fde->state & FDE_STATEMASK) | events;
-}
-
-void fdevent_set(fdevent* fde, unsigned events) {
-    check_main_thread();
-    events &= FDE_EVENTMASK;
-    if ((fde->state & FDE_EVENTMASK) == events) {
-        return;
-    }
-    CHECK(fde->state & FDE_ACTIVE);
-    fdevent_update(fde, events);
-    D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
-
-    if (fde->state & FDE_PENDING) {
-        // If we are pending, make sure we don't signal an event that is no longer wanted.
-        fde->events &= events;
-        if (fde->events == 0) {
-            g_pending_list.remove(fde);
-            fde->state &= ~FDE_PENDING;
-        }
-    }
-}
-
-void fdevent_add(fdevent* fde, unsigned events) {
-    check_main_thread();
-    CHECK(!(events & FDE_TIMEOUT));
-    fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
-}
-
-void fdevent_del(fdevent* fde, unsigned events) {
-    check_main_thread();
-    CHECK(!(events & FDE_TIMEOUT));
-    fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
-}
-
-void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
-    check_main_thread();
-    fde->timeout = timeout;
-    fde->last_active = std::chrono::steady_clock::now();
-}
-
-static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
-    std::string result;
-    for (const auto& pollfd : pollfds) {
-        std::string op;
-        if (pollfd.events & POLLIN) {
-            op += "R";
-        }
-        if (pollfd.events & POLLOUT) {
-            op += "W";
-        }
-        android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
-    }
-    return result;
-}
-
-static std::optional<std::chrono::milliseconds> calculate_timeout() {
-    std::optional<std::chrono::milliseconds> result = std::nullopt;
-    auto now = std::chrono::steady_clock::now();
-    check_main_thread();
-
-    for (const auto& [fd, pollnode] : g_poll_node_map) {
-        UNUSED(fd);
-        auto timeout_opt = pollnode.fde->timeout;
-        if (timeout_opt) {
-            auto deadline = pollnode.fde->last_active + *timeout_opt;
-            auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now);
-            if (time_left < std::chrono::milliseconds::zero()) {
-                time_left = std::chrono::milliseconds::zero();
-            }
-
-            if (!result) {
-                result = time_left;
-            } else {
-                result = std::min(*result, time_left);
-            }
-        }
-    }
-
-    return result;
-}
-
-static void fdevent_process() {
-    std::vector<adb_pollfd> pollfds;
-    for (const auto& pair : g_poll_node_map) {
-        pollfds.push_back(pair.second.pollfd);
-    }
-    CHECK_GT(pollfds.size(), 0u);
-    D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
-
-    auto timeout = calculate_timeout();
-    int timeout_ms;
-    if (!timeout) {
-        timeout_ms = -1;
-    } else {
-        timeout_ms = timeout->count();
-    }
-
-    int ret = adb_poll(&pollfds[0], pollfds.size(), timeout_ms);
-    if (ret == -1) {
-        PLOG(ERROR) << "poll(), ret = " << ret;
-        return;
-    }
-
-    auto post_poll = std::chrono::steady_clock::now();
-
-    for (const auto& pollfd : pollfds) {
-        if (pollfd.revents != 0) {
-            D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
-        }
-        unsigned events = 0;
-        if (pollfd.revents & POLLIN) {
-            events |= FDE_READ;
-        }
-        if (pollfd.revents & POLLOUT) {
-            events |= FDE_WRITE;
-        }
-        if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
-            // We fake a read, as the rest of the code assumes that errors will
-            // be detected at that point.
-            events |= FDE_READ | FDE_ERROR;
-        }
-#if defined(__linux__)
-        if (pollfd.revents & POLLRDHUP) {
-            events |= FDE_READ | FDE_ERROR;
-        }
-#endif
-        auto it = g_poll_node_map.find(pollfd.fd);
-        CHECK(it != g_poll_node_map.end());
-        fdevent* fde = it->second.fde;
-
-        if (events == 0) {
-            // Check for timeout.
-            if (fde->timeout) {
-                auto deadline = fde->last_active + *fde->timeout;
-                if (deadline < post_poll) {
-                    events |= FDE_TIMEOUT;
-                }
-            }
-        }
-
-        if (events != 0) {
-            CHECK_EQ(fde->fd.get(), pollfd.fd);
-            fde->events |= events;
-            fde->last_active = post_poll;
-            D("%s got events %x", dump_fde(fde).c_str(), events);
-            fde->state |= FDE_PENDING;
-            g_pending_list.push_back(fde);
-        }
-    }
-}
-
-template <class T>
-struct always_false : std::false_type {};
-
-static void fdevent_call_fdfunc(fdevent* fde) {
-    unsigned events = fde->events;
-    fde->events = 0;
-    CHECK(fde->state & FDE_PENDING);
-    fde->state &= (~FDE_PENDING);
-    D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
-    std::visit(
-            [&](auto&& f) {
-                using F = std::decay_t<decltype(f)>;
-                if constexpr (std::is_same_v<fd_func, F>) {
-                    f(fde->fd.get(), events, fde->arg);
-                } else if constexpr (std::is_same_v<fd_func2, F>) {
-                    f(fde, events, fde->arg);
-                } else {
-                    static_assert(always_false<F>::value, "non-exhaustive visitor");
-                }
-            },
-            fde->func);
-}
-
-static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
-    // We need to be careful around reentrancy here, since a function we call can queue up another
-    // function.
-    while (true) {
-        std::function<void()> fn;
-        {
-            std::lock_guard<std::mutex> lock(run_queue_mutex);
-            if (run_queue.empty()) {
-                break;
-            }
-            fn = run_queue.front();
-            run_queue.pop_front();
-        }
-        fn();
-    }
-}
-
-static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
-    CHECK_GE(fd, 0);
-    CHECK(ev & FDE_READ);
-
-    char buf[1024];
-
-    // Empty the fd.
-    if (adb_read(fd, buf, sizeof(buf)) == -1) {
-        PLOG(FATAL) << "failed to empty run queue notify fd";
-    }
-
-    // Mark that we need to flush, and then run it at the end of fdevent_loop.
-    run_needs_flush = true;
-}
-
-static void fdevent_run_setup() {
-    {
-        std::lock_guard<std::mutex> lock(run_queue_mutex);
-        CHECK(run_queue_notify_fd.get() == -1);
-        int s[2];
-        if (adb_socketpair(s) != 0) {
-            PLOG(FATAL) << "failed to create run queue notify socketpair";
-        }
-
-        if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
-            PLOG(FATAL) << "failed to make run queue notify socket nonblocking";
-        }
-
-        run_queue_notify_fd.reset(s[0]);
-        fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
-        CHECK(fde != nullptr);
-        fdevent_add(fde, FDE_READ);
-    }
-
-    fdevent_run_flush();
-}
-
-void fdevent_run_on_main_thread(std::function<void()> fn) {
-    std::lock_guard<std::mutex> lock(run_queue_mutex);
-    run_queue.push_back(std::move(fn));
-
-    // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
-    // In that case, rely on the setup code to flush the queue without a notification being needed.
-    if (run_queue_notify_fd != -1) {
-        int rc = adb_write(run_queue_notify_fd.get(), "", 1);
-
-        // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
-        if (rc == 0) {
-            PLOG(FATAL) << "run queue notify fd was closed?";
-        } else if (rc == -1 && errno != EAGAIN) {
-            PLOG(FATAL) << "failed to write to run queue notify fd";
-        }
-    }
-}
-
-static void fdevent_check_spin(uint64_t cycle) {
-    // Check to see if we're spinning because we forgot about an fdevent
-    // by keeping track of how long fdevents have been continuously pending.
-    struct SpinCheck {
-        fdevent* fde;
-        android::base::boot_clock::time_point timestamp;
-        uint64_t cycle;
-    };
-    static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
-    static auto last_cycle = android::base::boot_clock::now();
-
-    auto now = android::base::boot_clock::now();
-    if (now - last_cycle > 10ms) {
-        // We're not spinning.
-        g_continuously_pending.clear();
-        last_cycle = now;
-        return;
-    }
-    last_cycle = now;
-
-    for (auto* fde : g_pending_list) {
-        auto it = g_continuously_pending.find(fde->id);
-        if (it == g_continuously_pending.end()) {
-            g_continuously_pending[fde->id] =
-                    SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
-        } else {
-            it->second.cycle = cycle;
-        }
-    }
-
-    for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
-        if (it->second.cycle != cycle) {
-            it = g_continuously_pending.erase(it);
-        } else {
-            // Use an absurdly long window, since all we really care about is
-            // getting a bugreport eventually.
-            if (now - it->second.timestamp > 300s) {
-                LOG(FATAL_WITHOUT_ABORT)
-                        << "detected spin in fdevent: " << dump_fde(it->second.fde);
-#if defined(__linux__)
-                int fd = it->second.fde->fd.get();
-                std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
-                std::string path;
-                if (!android::base::Readlink(fd_path, &path)) {
-                    PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
-                }
-                LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
-#endif
-                abort();
-            }
-            ++it;
-        }
-    }
-}
-
-void fdevent_loop() {
-    set_main_thread();
-    fdevent_run_setup();
-
-    uint64_t cycle = 0;
-    while (true) {
-        if (terminate_loop) {
-            return;
-        }
-
-        D("--- --- waiting for events");
-
-        fdevent_process();
-
-        fdevent_check_spin(cycle++);
-
-        while (!g_pending_list.empty()) {
-            fdevent* fde = g_pending_list.front();
-            g_pending_list.pop_front();
-            fdevent_call_fdfunc(fde);
-        }
-
-        if (run_needs_flush) {
-            fdevent_run_flush();
-            run_needs_flush = false;
-        }
-    }
-}
-
-void fdevent_terminate_loop() {
-    terminate_loop = true;
-}
-
-size_t fdevent_installed_count() {
-    return g_poll_node_map.size();
-}
-
-void fdevent_reset() {
-    g_poll_node_map.clear();
-    g_pending_list.clear();
-
-    std::lock_guard<std::mutex> lock(run_queue_mutex);
-    run_queue_notify_fd.reset();
-    run_queue.clear();
-
-    main_thread_valid = false;
-    terminate_loop = false;
-}
diff --git a/adb/fdevent.h b/adb/fdevent.h
deleted file mode 100644
index 42dbb9e..0000000
--- a/adb/fdevent.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __FDEVENT_H
-#define __FDEVENT_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <chrono>
-#include <functional>
-#include <optional>
-#include <variant>
-
-#include "adb_unique_fd.h"
-
-// Events that may be observed
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-#define FDE_TIMEOUT 0x0008
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata);
-
-struct fdevent {
-    uint64_t id;
-
-    unique_fd fd;
-    int force_eof = 0;
-
-    uint16_t state = 0;
-    uint16_t events = 0;
-    std::optional<std::chrono::milliseconds> timeout;
-    std::chrono::steady_clock::time_point last_active;
-
-    std::variant<fd_func, fd_func2> func;
-    void* arg = nullptr;
-};
-
-// Allocate and initialize a new fdevent object
-// TODO: Switch these to unique_fd.
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
-
-// Deallocate an fdevent object that was created by fdevent_create.
-void fdevent_destroy(fdevent *fde);
-
-// fdevent_destroy, except releasing the file descriptor previously owned by the fdevent.
-unique_fd fdevent_release(fdevent* fde);
-
-// Change which events should cause notifications
-void fdevent_set(fdevent *fde, unsigned events);
-void fdevent_add(fdevent *fde, unsigned events);
-void fdevent_del(fdevent *fde, unsigned events);
-
-// Set a timeout on an fdevent.
-// If no events are triggered by the timeout, an FDE_TIMEOUT will be generated.
-// Note timeouts are not defused automatically; if a timeout is set on an fdevent, it will
-// trigger repeatedly every |timeout| ms.
-void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
-
-// Loop forever, handling events.
-void fdevent_loop();
-
-void check_main_thread();
-
-// Queue an operation to run on the main thread.
-void fdevent_run_on_main_thread(std::function<void()> fn);
-
-// The following functions are used only for tests.
-void fdevent_terminate_loop();
-size_t fdevent_installed_count();
-void fdevent_reset();
-void set_main_thread();
-
-#endif
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
new file mode 100644
index 0000000..562f587
--- /dev/null
+++ b/adb/fdevent/fdevent.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2006, Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG FDEVENT
+
+#include "sysdeps.h"
+
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include "adb_utils.h"
+#include "fdevent.h"
+#include "fdevent_epoll.h"
+#include "fdevent_poll.h"
+
+using namespace std::chrono_literals;
+using std::chrono::duration_cast;
+
+void invoke_fde(struct fdevent* fde, unsigned events) {
+    if (auto f = std::get_if<fd_func>(&fde->func)) {
+        (*f)(fde->fd.get(), events, fde->arg);
+    } else if (auto f = std::get_if<fd_func2>(&fde->func)) {
+        (*f)(fde, events, fde->arg);
+    } else {
+        __builtin_unreachable();
+    }
+}
+
+std::string dump_fde(const fdevent* fde) {
+    std::string state;
+    if (fde->state & FDE_READ) {
+        state += "R";
+    }
+    if (fde->state & FDE_WRITE) {
+        state += "W";
+    }
+    if (fde->state & FDE_ERROR) {
+        state += "E";
+    }
+    return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+                                       state.c_str());
+}
+
+fdevent* fdevent_context::Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg) {
+    CheckMainThread();
+    CHECK_GE(fd.get(), 0);
+
+    int fd_num = fd.get();
+
+    fdevent* fde = new fdevent();
+    fde->id = fdevent_id_++;
+    fde->state = 0;
+    fde->fd = std::move(fd);
+    fde->func = func;
+    fde->arg = arg;
+    if (!set_file_block_mode(fde->fd, false)) {
+        // Here is not proper to handle the error. If it fails here, some error is
+        // likely to be detected by poll(), then we can let the callback function
+        // to handle it.
+        LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
+    }
+
+    auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fde);
+    CHECK(inserted);
+    UNUSED(it);
+
+    this->Register(fde);
+    return fde;
+}
+
+unique_fd fdevent_context::Destroy(fdevent* fde) {
+    CheckMainThread();
+    if (!fde) {
+        return {};
+    }
+
+    this->Unregister(fde);
+
+    auto erased = this->installed_fdevents_.erase(fde->fd.get());
+    CHECK_EQ(1UL, erased);
+
+    unique_fd result = std::move(fde->fd);
+    delete fde;
+    return result;
+}
+
+void fdevent_context::Add(fdevent* fde, unsigned events) {
+    CHECK(!(events & FDE_TIMEOUT));
+    Set(fde, fde->state | events);
+}
+
+void fdevent_context::Del(fdevent* fde, unsigned events) {
+    CHECK(!(events & FDE_TIMEOUT));
+    Set(fde, fde->state & ~events);
+}
+
+void fdevent_context::SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
+    CheckMainThread();
+    fde->timeout = timeout;
+    fde->last_active = std::chrono::steady_clock::now();
+}
+
+std::optional<std::chrono::milliseconds> fdevent_context::CalculatePollDuration() {
+    std::optional<std::chrono::milliseconds> result = std::nullopt;
+    auto now = std::chrono::steady_clock::now();
+    CheckMainThread();
+
+    for (const auto& [fd, fde] : this->installed_fdevents_) {
+        UNUSED(fd);
+        auto timeout_opt = fde->timeout;
+        if (timeout_opt) {
+            auto deadline = fde->last_active + *timeout_opt;
+            auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now);
+            if (time_left < 0ms) {
+                time_left = 0ms;
+            }
+
+            if (!result) {
+                result = time_left;
+            } else {
+                result = std::min(*result, time_left);
+            }
+        }
+    }
+
+    return result;
+}
+
+void fdevent_context::HandleEvents(const std::vector<fdevent_event>& events) {
+    for (const auto& event : events) {
+        invoke_fde(event.fde, event.events);
+    }
+    FlushRunQueue();
+}
+
+void fdevent_context::FlushRunQueue() {
+    // We need to be careful around reentrancy here, since a function we call can queue up another
+    // function.
+    while (true) {
+        std::function<void()> fn;
+        {
+            std::lock_guard<std::mutex> lock(this->run_queue_mutex_);
+            if (this->run_queue_.empty()) {
+                break;
+            }
+            fn = std::move(this->run_queue_.front());
+            this->run_queue_.pop_front();
+        }
+        fn();
+    }
+}
+
+void fdevent_context::CheckMainThread() {
+    if (main_thread_id_) {
+        CHECK_EQ(*main_thread_id_, android::base::GetThreadId());
+    }
+}
+
+void fdevent_context::Run(std::function<void()> fn) {
+    {
+        std::lock_guard<std::mutex> lock(run_queue_mutex_);
+        run_queue_.push_back(std::move(fn));
+    }
+
+    Interrupt();
+}
+
+void fdevent_context::TerminateLoop() {
+    terminate_loop_ = true;
+    Interrupt();
+}
+
+static std::unique_ptr<fdevent_context> fdevent_create_context() {
+#if defined(__linux__)
+    return std::make_unique<fdevent_context_epoll>();
+#else
+    return std::make_unique<fdevent_context_poll>();
+#endif
+}
+
+static auto& g_ambient_fdevent_context =
+        *new std::unique_ptr<fdevent_context>(fdevent_create_context());
+
+static fdevent_context* fdevent_get_ambient() {
+    return g_ambient_fdevent_context.get();
+}
+
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
+    unique_fd ufd(fd);
+    return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
+}
+
+fdevent* fdevent_create(int fd, fd_func2 func, void* arg) {
+    unique_fd ufd(fd);
+    return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
+}
+
+unique_fd fdevent_release(fdevent* fde) {
+    return fdevent_get_ambient()->Destroy(fde);
+}
+
+void fdevent_destroy(fdevent* fde) {
+    fdevent_get_ambient()->Destroy(fde);
+}
+
+void fdevent_set(fdevent* fde, unsigned events) {
+    fdevent_get_ambient()->Set(fde, events);
+}
+
+void fdevent_add(fdevent* fde, unsigned events) {
+    fdevent_get_ambient()->Add(fde, events);
+}
+
+void fdevent_del(fdevent* fde, unsigned events) {
+    fdevent_get_ambient()->Del(fde, events);
+}
+
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
+    fdevent_get_ambient()->SetTimeout(fde, timeout);
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+    fdevent_get_ambient()->Run(std::move(fn));
+}
+
+void fdevent_loop() {
+    fdevent_get_ambient()->Loop();
+}
+
+void check_main_thread() {
+    fdevent_get_ambient()->CheckMainThread();
+}
+
+void fdevent_terminate_loop() {
+    fdevent_get_ambient()->TerminateLoop();
+}
+
+size_t fdevent_installed_count() {
+    return fdevent_get_ambient()->InstalledCount();
+}
+
+void fdevent_reset() {
+    g_ambient_fdevent_context = fdevent_create_context();
+}
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
new file mode 100644
index 0000000..86814d7
--- /dev/null
+++ b/adb/fdevent/fdevent.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FDEVENT_H
+#define __FDEVENT_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <atomic>
+#include <chrono>
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <variant>
+
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+
+// Events that may be observed
+#define FDE_READ 0x0001
+#define FDE_WRITE 0x0002
+#define FDE_ERROR 0x0004
+#define FDE_TIMEOUT 0x0008
+
+struct fdevent;
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata);
+
+void invoke_fde(struct fdevent* fde, unsigned events);
+std::string dump_fde(const fdevent* fde);
+
+struct fdevent_event {
+    fdevent* fde;
+    unsigned events;
+};
+
+struct fdevent_context {
+  public:
+    virtual ~fdevent_context() = default;
+
+    // Allocate and initialize a new fdevent object.
+    fdevent* Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg);
+
+    // Deallocate an fdevent object, returning the file descriptor that was owned by it.
+    // Note that this calls Set, which is a virtual method, so destructors that call this must be
+    // final.
+    unique_fd Destroy(fdevent* fde);
+
+  protected:
+    virtual void Register(fdevent*) {}
+    virtual void Unregister(fdevent*) {}
+
+  public:
+    // Change which events should cause notifications.
+    virtual void Set(fdevent* fde, unsigned events) = 0;
+    void Add(fdevent* fde, unsigned events);
+    void Del(fdevent* fde, unsigned events);
+
+    // Set a timeout on an fdevent.
+    // If no events are triggered by the timeout, an FDE_TIMEOUT will be generated.
+    // Note timeouts are not defused automatically; if a timeout is set on an fdevent, it will
+    // trigger repeatedly every |timeout| ms.
+    void SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
+
+  protected:
+    std::optional<std::chrono::milliseconds> CalculatePollDuration();
+    void HandleEvents(const std::vector<fdevent_event>& events);
+
+  private:
+    // Run all pending functions enqueued via Run().
+    void FlushRunQueue() EXCLUDES(run_queue_mutex_);
+
+  public:
+    // Loop until TerminateLoop is called, handling events.
+    // Implementations should call FlushRunQueue on every iteration, and check the value of
+    // terminate_loop_ to determine whether to stop.
+    virtual void Loop() = 0;
+
+    // Assert that the caller is either running on the context's main thread, or that there is no
+    // active main thread.
+    void CheckMainThread();
+
+    // Queue an operation to be run on the main thread.
+    void Run(std::function<void()> fn);
+
+    // Test-only functionality:
+    void TerminateLoop();
+    virtual size_t InstalledCount() = 0;
+
+  protected:
+    // Interrupt the run loop.
+    virtual void Interrupt() = 0;
+
+    std::optional<uint64_t> main_thread_id_ = std::nullopt;
+    std::atomic<bool> terminate_loop_ = false;
+
+  protected:
+    std::unordered_map<int, fdevent*> installed_fdevents_;
+
+  private:
+    uint64_t fdevent_id_ = 0;
+    std::mutex run_queue_mutex_;
+    std::deque<std::function<void()>> run_queue_ GUARDED_BY(run_queue_mutex_);
+};
+
+struct fdevent {
+    uint64_t id;
+
+    unique_fd fd;
+    int force_eof = 0;
+
+    uint16_t state = 0;
+    std::optional<std::chrono::milliseconds> timeout;
+    std::chrono::steady_clock::time_point last_active;
+
+    std::variant<fd_func, fd_func2> func;
+    void* arg = nullptr;
+};
+
+// Backwards compatibility shims that forward to the global fdevent_context.
+fdevent* fdevent_create(int fd, fd_func func, void* arg);
+fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
+
+unique_fd fdevent_release(fdevent* fde);
+void fdevent_destroy(fdevent* fde);
+
+void fdevent_set(fdevent *fde, unsigned events);
+void fdevent_add(fdevent *fde, unsigned events);
+void fdevent_del(fdevent *fde, unsigned events);
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
+void fdevent_loop();
+void check_main_thread();
+
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
+// The following functions are used only for tests.
+void fdevent_terminate_loop();
+size_t fdevent_installed_count();
+void fdevent_reset();
+
+#endif
diff --git a/adb/fdevent/fdevent_epoll.cpp b/adb/fdevent/fdevent_epoll.cpp
new file mode 100644
index 0000000..e3d1674
--- /dev/null
+++ b/adb/fdevent/fdevent_epoll.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fdevent_epoll.h"
+
+#if defined(__linux__)
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <android-base/logging.h>
+#include <android-base/threads.h>
+
+#include "adb_unique_fd.h"
+#include "fdevent.h"
+
+static void fdevent_interrupt(int fd, unsigned, void*) {
+    uint64_t buf;
+    ssize_t rc = TEMP_FAILURE_RETRY(adb_read(fd, &buf, sizeof(buf)));
+    if (rc == -1) {
+        PLOG(FATAL) << "failed to read from fdevent interrupt fd";
+    }
+}
+
+fdevent_context_epoll::fdevent_context_epoll() {
+    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+
+    unique_fd interrupt_fd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+    if (interrupt_fd == -1) {
+        PLOG(FATAL) << "failed to create fdevent interrupt eventfd";
+    }
+
+    unique_fd interrupt_fd_dup(fcntl(interrupt_fd.get(), F_DUPFD_CLOEXEC, 3));
+    if (interrupt_fd_dup == -1) {
+        PLOG(FATAL) << "failed to dup fdevent interrupt eventfd";
+    }
+
+    this->interrupt_fd_ = std::move(interrupt_fd_dup);
+    fdevent* fde = this->Create(std::move(interrupt_fd), fdevent_interrupt, nullptr);
+    CHECK(fde != nullptr);
+    this->Add(fde, FDE_READ);
+}
+
+fdevent_context_epoll::~fdevent_context_epoll() {
+    // Destroy calls virtual methods, but this class is final, so that's okay.
+    this->Destroy(this->interrupt_fde_);
+}
+
+static epoll_event calculate_epoll_event(fdevent* fde) {
+    epoll_event result;
+    result.events = 0;
+    if (fde->state & FDE_READ) {
+        result.events |= EPOLLIN;
+    }
+    if (fde->state & FDE_WRITE) {
+        result.events |= EPOLLOUT;
+    }
+    if (fde->state & FDE_ERROR) {
+        result.events |= EPOLLERR;
+    }
+    result.events |= EPOLLRDHUP;
+    result.data.ptr = fde;
+    return result;
+}
+
+void fdevent_context_epoll::Register(fdevent* fde) {
+    epoll_event ev = calculate_epoll_event(fde);
+    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fde->fd.get(), &ev) != 0) {
+        PLOG(FATAL) << "failed to register fd " << fde->fd.get() << " with epoll";
+    }
+}
+
+void fdevent_context_epoll::Unregister(fdevent* fde) {
+    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, fde->fd.get(), nullptr) != 0) {
+        PLOG(FATAL) << "failed to unregister fd " << fde->fd.get() << " with epoll";
+    }
+}
+
+void fdevent_context_epoll::Set(fdevent* fde, unsigned events) {
+    unsigned previous_state = fde->state;
+    fde->state = events;
+
+    // If the state is the same, or only differed by FDE_TIMEOUT, we don't need to modify epoll.
+    if ((previous_state & ~FDE_TIMEOUT) == (events & ~FDE_TIMEOUT)) {
+        return;
+    }
+
+    epoll_event ev = calculate_epoll_event(fde);
+    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, fde->fd.get(), &ev) != 0) {
+        PLOG(FATAL) << "failed to modify fd " << fde->fd.get() << " with epoll";
+    }
+}
+
+void fdevent_context_epoll::Loop() {
+    main_thread_id_ = android::base::GetThreadId();
+
+    std::vector<fdevent_event> fde_events;
+    std::vector<epoll_event> epoll_events;
+    epoll_events.resize(this->installed_fdevents_.size());
+
+    while (true) {
+        if (terminate_loop_) {
+            break;
+        }
+
+        int rc = -1;
+        while (rc == -1) {
+            std::optional<std::chrono::milliseconds> timeout = CalculatePollDuration();
+            int timeout_ms;
+            if (!timeout) {
+                timeout_ms = -1;
+            } else {
+                timeout_ms = timeout->count();
+            }
+
+            rc = epoll_wait(epoll_fd_.get(), epoll_events.data(), epoll_events.size(), timeout_ms);
+            if (rc == -1 && errno != EINTR) {
+                PLOG(FATAL) << "epoll_wait failed";
+            }
+        }
+
+        auto post_poll = std::chrono::steady_clock::now();
+        std::unordered_map<fdevent*, unsigned> event_map;
+        for (int i = 0; i < rc; ++i) {
+            fdevent* fde = static_cast<fdevent*>(epoll_events[i].data.ptr);
+
+            unsigned events = 0;
+            if (epoll_events[i].events & EPOLLIN) {
+                CHECK(fde->state & FDE_READ);
+                events |= FDE_READ;
+            }
+            if (epoll_events[i].events & EPOLLOUT) {
+                CHECK(fde->state & FDE_WRITE);
+                events |= FDE_WRITE;
+            }
+            if (epoll_events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
+                // We fake a read, as the rest of the code assumes that errors will
+                // be detected at that point.
+                events |= FDE_READ | FDE_ERROR;
+            }
+
+            event_map[fde] = events;
+        }
+
+        for (const auto& [fd, fde] : installed_fdevents_) {
+            unsigned events = 0;
+            if (auto it = event_map.find(fde); it != event_map.end()) {
+                events = it->second;
+            }
+
+            if (events == 0) {
+                if (fde->timeout) {
+                    auto deadline = fde->last_active + *fde->timeout;
+                    if (deadline < post_poll) {
+                        events |= FDE_TIMEOUT;
+                    }
+                }
+            }
+
+            if (events != 0) {
+                LOG(DEBUG) << dump_fde(fde) << " got events " << std::hex << std::showbase
+                           << events;
+                fde_events.push_back({fde, events});
+                fde->last_active = post_poll;
+            }
+        }
+        this->HandleEvents(std::move(fde_events));
+        fde_events.clear();
+    }
+
+    main_thread_id_.reset();
+}
+
+size_t fdevent_context_epoll::InstalledCount() {
+    // We always have an installed fde for interrupt.
+    return this->installed_fdevents_.size() - 1;
+}
+
+void fdevent_context_epoll::Interrupt() {
+    uint64_t i = 1;
+    ssize_t rc = TEMP_FAILURE_RETRY(adb_write(this->interrupt_fd_, &i, sizeof(i)));
+    if (rc != sizeof(i)) {
+        PLOG(FATAL) << "failed to write to fdevent interrupt eventfd";
+    }
+}
+
+#endif  // defined(__linux__)
diff --git a/adb/fdevent/fdevent_epoll.h b/adb/fdevent/fdevent_epoll.h
new file mode 100644
index 0000000..684fa32
--- /dev/null
+++ b/adb/fdevent/fdevent_epoll.h
@@ -0,0 +1,61 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__linux__)
+
+#include "sysdeps.h"
+
+#include <sys/epoll.h>
+
+#include <deque>
+#include <list>
+#include <mutex>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "fdevent.h"
+
+struct fdevent_context_epoll final : public fdevent_context {
+    fdevent_context_epoll();
+    virtual ~fdevent_context_epoll();
+
+    virtual void Register(fdevent* fde) final;
+    virtual void Unregister(fdevent* fde) final;
+
+    virtual void Set(fdevent* fde, unsigned events) final;
+
+    virtual void Loop() final;
+    size_t InstalledCount() final;
+
+  protected:
+    virtual void Interrupt() final;
+
+  public:
+    // All operations to fdevent should happen only in the main thread.
+    // That's why we don't need a lock for fdevent.
+    std::unordered_map<int, fdevent*> epoll_node_map_;
+    std::list<fdevent*> pending_list_;
+
+    unique_fd epoll_fd_;
+    unique_fd interrupt_fd_;
+    fdevent* interrupt_fde_ = nullptr;
+};
+
+#endif  // defined(__linux__)
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
new file mode 100644
index 0000000..cc4a7a1
--- /dev/null
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG FDEVENT
+
+#include "sysdeps.h"
+#include "fdevent_poll.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <deque>
+#include <functional>
+#include <list>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "fdevent.h"
+#include "sysdeps/chrono.h"
+
+static void fdevent_interrupt(int fd, unsigned, void*) {
+    char buf[BUFSIZ];
+    ssize_t rc = TEMP_FAILURE_RETRY(adb_read(fd, buf, sizeof(buf)));
+    if (rc == -1) {
+        PLOG(FATAL) << "failed to read from fdevent interrupt fd";
+    }
+}
+
+fdevent_context_poll::fdevent_context_poll() {
+    int s[2];
+    if (adb_socketpair(s) != 0) {
+        PLOG(FATAL) << "failed to create fdevent interrupt socketpair";
+    }
+
+    if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
+        PLOG(FATAL) << "failed to make fdevent interrupt socket nonblocking";
+    }
+
+    this->interrupt_fd_.reset(s[0]);
+    fdevent* fde = this->Create(unique_fd(s[1]), fdevent_interrupt, nullptr);
+    CHECK(fde != nullptr);
+    this->Add(fde, FDE_READ);
+}
+
+fdevent_context_poll::~fdevent_context_poll() {
+    // Destroy calls virtual methods, but this class is final, so that's okay.
+    this->Destroy(this->interrupt_fde_);
+}
+
+void fdevent_context_poll::Set(fdevent* fde, unsigned events) {
+    CheckMainThread();
+    fde->state = events;
+    D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
+}
+
+static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
+    std::string result;
+    for (const auto& pollfd : pollfds) {
+        std::string op;
+        if (pollfd.events & POLLIN) {
+            op += "R";
+        }
+        if (pollfd.events & POLLOUT) {
+            op += "W";
+        }
+        android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
+    }
+    return result;
+}
+
+void fdevent_context_poll::Loop() {
+    main_thread_id_ = android::base::GetThreadId();
+
+    while (true) {
+        if (terminate_loop_) {
+            break;
+        }
+
+        D("--- --- waiting for events");
+        std::vector<adb_pollfd> pollfds;
+        for (const auto& [fd, fde] : this->installed_fdevents_) {
+            adb_pollfd pfd;
+            pfd.fd = fd;
+            pfd.events = 0;
+            if (fde->state & FDE_READ) {
+                pfd.events |= POLLIN;
+            }
+            if (fde->state & FDE_WRITE) {
+                pfd.events |= POLLOUT;
+            }
+            if (fde->state & FDE_ERROR) {
+                pfd.events |= POLLERR;
+            }
+#if defined(__linux__)
+            pfd.events |= POLLRDHUP;
+#endif
+            pfd.revents = 0;
+            pollfds.push_back(pfd);
+        }
+        CHECK_GT(pollfds.size(), 0u);
+        D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+
+        std::optional<std::chrono::milliseconds> timeout = CalculatePollDuration();
+        int timeout_ms;
+        if (!timeout) {
+            timeout_ms = -1;
+        } else {
+            timeout_ms = timeout->count();
+        }
+
+        int ret = adb_poll(pollfds.data(), pollfds.size(), timeout_ms);
+        if (ret == -1) {
+            PLOG(ERROR) << "poll(), ret = " << ret;
+            return;
+        }
+
+        auto post_poll = std::chrono::steady_clock::now();
+        std::vector<fdevent_event> poll_events;
+
+        for (const auto& pollfd : pollfds) {
+            unsigned events = 0;
+            if (pollfd.revents & POLLIN) {
+                events |= FDE_READ;
+            }
+            if (pollfd.revents & POLLOUT) {
+                events |= FDE_WRITE;
+            }
+            if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+                // We fake a read, as the rest of the code assumes that errors will
+                // be detected at that point.
+                events |= FDE_READ | FDE_ERROR;
+            }
+#if defined(__linux__)
+            if (pollfd.revents & POLLRDHUP) {
+                events |= FDE_READ | FDE_ERROR;
+            }
+#endif
+
+            auto it = this->installed_fdevents_.find(pollfd.fd);
+            CHECK(it != this->installed_fdevents_.end());
+            fdevent* fde = it->second;
+
+            if (events == 0) {
+                if (fde->timeout) {
+                    auto deadline = fde->last_active + *fde->timeout;
+                    if (deadline < post_poll) {
+                        events |= FDE_TIMEOUT;
+                    }
+                }
+            }
+
+            if (events != 0) {
+                D("%s got events %x", dump_fde(fde).c_str(), events);
+                poll_events.push_back({fde, events});
+                fde->last_active = post_poll;
+            }
+        }
+        this->HandleEvents(std::move(poll_events));
+    }
+
+    main_thread_id_.reset();
+}
+
+size_t fdevent_context_poll::InstalledCount() {
+    // We always have an installed fde for interrupt.
+    return this->installed_fdevents_.size() - 1;
+}
+
+void fdevent_context_poll::Interrupt() {
+    int rc = adb_write(this->interrupt_fd_, "", 1);
+
+    // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
+    if (rc == 0) {
+        PLOG(FATAL) << "fdevent interrupt fd was closed?";
+    } else if (rc == -1 && errno != EAGAIN) {
+        PLOG(FATAL) << "failed to write to fdevent interrupt fd";
+    }
+}
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
new file mode 100644
index 0000000..98abab2
--- /dev/null
+++ b/adb/fdevent/fdevent_poll.h
@@ -0,0 +1,63 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps.h"
+
+#include <deque>
+#include <list>
+#include <mutex>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "fdevent.h"
+
+struct PollNode {
+  fdevent* fde;
+  adb_pollfd pollfd;
+
+  explicit PollNode(fdevent* fde) : fde(fde) {
+      memset(&pollfd, 0, sizeof(pollfd));
+      pollfd.fd = fde->fd.get();
+
+#if defined(__linux__)
+      // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
+      // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
+      pollfd.events = POLLRDHUP;
+#endif
+  }
+};
+
+struct fdevent_context_poll final : public fdevent_context {
+    fdevent_context_poll();
+    virtual ~fdevent_context_poll();
+
+    virtual void Set(fdevent* fde, unsigned events) final;
+
+    virtual void Loop() final;
+
+    virtual size_t InstalledCount() final;
+
+  protected:
+    virtual void Interrupt() final;
+
+  public:
+    unique_fd interrupt_fd_;
+    fdevent* interrupt_fde_ = nullptr;
+};
diff --git a/adb/fdevent_test.cpp b/adb/fdevent/fdevent_test.cpp
similarity index 87%
rename from adb/fdevent_test.cpp
rename to adb/fdevent/fdevent_test.cpp
index 682f061..e06b3b3 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent/fdevent_test.cpp
@@ -118,8 +118,8 @@
 TEST_F(FdeventTest, smoke) {
     for (bool use_new_callback : {true, false}) {
         fdevent_reset();
-        const size_t PIPE_COUNT = 10;
-        const size_t MESSAGE_LOOP_COUNT = 100;
+        const size_t PIPE_COUNT = 512;
+        const size_t MESSAGE_LOOP_COUNT = 10;
         const std::string MESSAGE = "fdevent_test";
         int fd_pair1[2];
         int fd_pair2[2];
@@ -172,44 +172,6 @@
     }
 }
 
-struct InvalidFdArg {
-    fdevent* fde;
-    unsigned expected_events;
-    size_t* happened_event_count;
-};
-
-static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
-    InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
-    ASSERT_EQ(arg->expected_events, events);
-    fdevent_destroy(arg->fde);
-    if (++*(arg->happened_event_count) == 2) {
-        fdevent_terminate_loop();
-    }
-}
-
-static void InvalidFdThreadFunc() {
-    const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
-    size_t happened_event_count = 0;
-    InvalidFdArg read_arg;
-    read_arg.expected_events = FDE_READ | FDE_ERROR;
-    read_arg.happened_event_count = &happened_event_count;
-    read_arg.fde = fdevent_create(INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
-    fdevent_add(read_arg.fde, FDE_READ);
-
-    const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
-    InvalidFdArg write_arg;
-    write_arg.expected_events = FDE_READ | FDE_ERROR;
-    write_arg.happened_event_count = &happened_event_count;
-    write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
-    fdevent_add(write_arg.fde, FDE_WRITE);
-    fdevent_loop();
-}
-
-TEST_F(FdeventTest, invalid_fd) {
-    std::thread thread(InvalidFdThreadFunc);
-    thread.join();
-}
-
 TEST_F(FdeventTest, run_on_main_thread) {
     std::vector<int> vec;
 
diff --git a/adb/fdevent_test.h b/adb/fdevent/fdevent_test.h
similarity index 95%
rename from adb/fdevent_test.h
rename to adb/fdevent/fdevent_test.h
index 24bce59..2139d0f 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent/fdevent_test.h
@@ -78,8 +78,8 @@
     }
 
     size_t GetAdditionalLocalSocketCount() {
-        // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
-        return 2;
+        // dummy socket installed in PrepareThread()
+        return 1;
     }
 
     void TerminateThread() {
diff --git a/adb/socket.h b/adb/socket.h
index b8c559a..4276851 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -24,7 +24,7 @@
 #include <string>
 
 #include "adb_unique_fd.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
 #include "types.h"
 
 class atransport;
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 1333724..98468b5 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -93,7 +93,7 @@
         }
     } else {
         std::string addr(spec.substr(4));
-        port_value = -1;
+        port_value = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
 
         // FIXME: ParseNetAddress rejects port 0. This currently doesn't hurt, because listening
         //        on an address that isn't 'localhost' is unsupported.
diff --git a/adb/socket_spec_test.cpp b/adb/socket_spec_test.cpp
index f5ec0f1..3a2f60c 100644
--- a/adb/socket_spec_test.cpp
+++ b/adb/socket_spec_test.cpp
@@ -20,38 +20,71 @@
 
 #include <gtest/gtest.h>
 
-TEST(socket_spec, parse_tcp_socket_spec) {
+TEST(socket_spec, parse_tcp_socket_spec_just_port) {
     std::string hostname, error, serial;
     int port;
     EXPECT_TRUE(parse_tcp_socket_spec("tcp:5037", &hostname, &port, &serial, &error));
     EXPECT_EQ("", hostname);
     EXPECT_EQ(5037, port);
     EXPECT_EQ("", serial);
+}
 
-    // Bad ports:
+TEST(socket_spec, parse_tcp_socket_spec_bad_ports) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:-1", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:65536", &hostname, &port, &serial, &error));
+}
 
+TEST(socket_spec, parse_tcp_socket_spec_host_and_port) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("localhost", hostname);
     EXPECT_EQ(1234, port);
     EXPECT_EQ("localhost:1234", serial);
+}
 
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
+TEST(socket_spec, parse_tcp_socket_spec_host_no_port) {
+    std::string hostname, error, serial;
+    int port;
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:localhost", &hostname, &port, &serial, &error));
+    EXPECT_EQ("localhost", hostname);
+    EXPECT_EQ(5555, port);
+    EXPECT_EQ("localhost:5555", serial);
+}
+
+TEST(socket_spec, parse_tcp_socket_spec_host_bad_ports) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:-1", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:localhost:65536", &hostname, &port, &serial, &error));
+}
 
-    // IPv6:
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_and_port) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_TRUE(parse_tcp_socket_spec("tcp:[::1]:1234", &hostname, &port, &serial, &error));
     EXPECT_EQ("::1", hostname);
     EXPECT_EQ(1234, port);
     EXPECT_EQ("[::1]:1234", serial);
+}
 
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_no_port) {
+    std::string hostname, error, serial;
+    int port;
+    EXPECT_TRUE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
+    EXPECT_EQ("::1", hostname);
+    EXPECT_EQ(5555, port);
+    EXPECT_EQ("[::1]:5555", serial);
+}
+
+TEST(socket_spec, parse_tcp_socket_spec_ipv6_bad_ports) {
+    std::string hostname, error, serial;
+    int port;
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:", &hostname, &port, &serial, &error));
     EXPECT_FALSE(parse_tcp_socket_spec("tcp:[::1]:-1", &hostname, &port, &serial, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1", &hostname, &port, &serial, &error));
-    EXPECT_FALSE(parse_tcp_socket_spec("tcp:::1:1234", &hostname, &port, &serial, &error));
 }
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 5e28f76..1601ff0 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
 
 #include <gtest/gtest.h>
 
@@ -29,7 +29,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
-#include "fdevent_test.h"
+#include "fdevent/fdevent_test.h"
 #include "socket.h"
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 78abba5..987f994 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -33,6 +33,7 @@
 // Include this before open/close/unlink are defined as macros below.
 #include <android-base/errors.h>
 #include <android-base/macros.h>
+#include <android-base/off64_t.h>
 #include <android-base/unique_fd.h>
 #include <android-base/utf8.h>
 
@@ -64,8 +65,6 @@
 #include <memory>   // unique_ptr
 #include <string>
 
-#include "fdevent.h"
-
 #define OS_PATH_SEPARATORS "\\/"
 #define OS_PATH_SEPARATOR '\\'
 #define OS_PATH_SEPARATOR_STR "\\"
@@ -93,11 +92,14 @@
 extern int adb_open(const char* path, int options);
 extern int adb_creat(const char* path, int mode);
 extern int adb_read(borrowed_fd fd, void* buf, int len);
+extern int adb_pread(borrowed_fd fd, void* buf, int len, off64_t offset);
 extern int adb_write(borrowed_fd fd, const void* buf, int len);
+extern int adb_pwrite(borrowed_fd fd, const void* buf, int len, off64_t offset);
 extern int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where);
 extern int adb_shutdown(borrowed_fd fd, int direction = SHUT_RDWR);
 extern int adb_close(int fd);
 extern int adb_register_socket(SOCKET s);
+extern HANDLE adb_get_os_handle(borrowed_fd fd);
 
 // See the comments for the !defined(_WIN32) version of unix_close().
 static __inline__ int unix_close(int fd) {
@@ -117,6 +119,9 @@
 #undef   read
 #define  read  ___xxx_read
 
+#undef pread
+#define pread ___xxx_pread
+
 // See the comments for the !defined(_WIN32) version of unix_write().
 static __inline__ int unix_write(borrowed_fd fd, const void* buf, size_t len) {
     return write(fd.get(), buf, len);
@@ -124,6 +129,9 @@
 #undef   write
 #define  write  ___xxx_write
 
+#undef pwrite
+#define pwrite ___xxx_pwrite
+
 // See the comments for the !defined(_WIN32) version of unix_lseek().
 static __inline__ int unix_lseek(borrowed_fd fd, int pos, int where) {
     return lseek(fd.get(), pos, where);
@@ -417,6 +425,14 @@
     return TEMP_FAILURE_RETRY(read(fd.get(), buf, len));
 }
 
+static __inline__ int adb_pread(int fd, void* buf, size_t len, off64_t offset) {
+#if defined(__APPLE__)
+    return TEMP_FAILURE_RETRY(pread(fd, buf, len, offset));
+#else
+    return TEMP_FAILURE_RETRY(pread64(fd, buf, len, offset));
+#endif
+}
+
 // Like unix_read(), but does not handle EINTR.
 static __inline__ int unix_read_interruptible(borrowed_fd fd, void* buf, size_t len) {
     return read(fd.get(), buf, len);
@@ -424,12 +440,25 @@
 
 #undef read
 #define read ___xxx_read
+#undef pread
+#define pread ___xxx_pread
 
 static __inline__ int adb_write(borrowed_fd fd, const void* buf, size_t len) {
     return TEMP_FAILURE_RETRY(write(fd.get(), buf, len));
 }
+
+static __inline__ int adb_pwrite(int fd, const void* buf, size_t len, off64_t offset) {
+#if defined(__APPLE__)
+    return TEMP_FAILURE_RETRY(pwrite(fd, buf, len, offset));
+#else
+    return TEMP_FAILURE_RETRY(pwrite64(fd, buf, len, offset));
+#endif
+}
+
 #undef   write
 #define  write  ___xxx_write
+#undef pwrite
+#define pwrite ___xxx_pwrite
 
 static __inline__ int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
 #if defined(__APPLE__)
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
index 79cebe6..0f4b39c 100644
--- a/adb/sysdeps_test.cpp
+++ b/adb/sysdeps_test.cpp
@@ -25,6 +25,21 @@
 #include "sysdeps.h"
 #include "sysdeps/chrono.h"
 
+#if defined(_WIN32)
+#include <windows.h>
+static bool IsWine() {
+    HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
+    if (!ntdll) {
+        return false;
+    }
+    return GetProcAddress(ntdll, "wine_get_version") != nullptr;
+}
+#else
+static bool IsWine() {
+    return false;
+}
+#endif
+
 TEST(sysdeps_socketpair, smoke) {
     int fds[2];
     ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
@@ -182,8 +197,10 @@
 
     EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
 
-    // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
-    EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+    if (!IsWine()) {
+        // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
+        EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+    }
 }
 
 TEST_F(sysdeps_poll, fd_count) {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index f86cd03..4d6cf3d 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -60,6 +60,7 @@
     int (*_fh_read)(FH, void*, int);
     int (*_fh_write)(FH, const void*, int);
     int (*_fh_writev)(FH, const adb_iovec*, int);
+    intptr_t (*_fh_get_os_handle)(FH);
 } FHClassRec;
 
 static void _fh_file_init(FH);
@@ -68,14 +69,11 @@
 static int _fh_file_read(FH, void*, int);
 static int _fh_file_write(FH, const void*, int);
 static int _fh_file_writev(FH, const adb_iovec*, int);
+static intptr_t _fh_file_get_os_handle(FH f);
 
 static const FHClassRec _fh_file_class = {
-    _fh_file_init,
-    _fh_file_close,
-    _fh_file_lseek,
-    _fh_file_read,
-    _fh_file_write,
-    _fh_file_writev,
+        _fh_file_init,  _fh_file_close,  _fh_file_lseek,         _fh_file_read,
+        _fh_file_write, _fh_file_writev, _fh_file_get_os_handle,
 };
 
 static void _fh_socket_init(FH);
@@ -84,14 +82,11 @@
 static int _fh_socket_read(FH, void*, int);
 static int _fh_socket_write(FH, const void*, int);
 static int _fh_socket_writev(FH, const adb_iovec*, int);
+static intptr_t _fh_socket_get_os_handle(FH f);
 
 static const FHClassRec _fh_socket_class = {
-    _fh_socket_init,
-    _fh_socket_close,
-    _fh_socket_lseek,
-    _fh_socket_read,
-    _fh_socket_write,
-    _fh_socket_writev,
+        _fh_socket_init,  _fh_socket_close,  _fh_socket_lseek,         _fh_socket_read,
+        _fh_socket_write, _fh_socket_writev, _fh_socket_get_os_handle,
 };
 
 #if defined(assert)
@@ -331,6 +326,10 @@
     return li.QuadPart;
 }
 
+static intptr_t _fh_file_get_os_handle(FH f) {
+    return reinterpret_cast<intptr_t>(f->u.handle);
+}
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -456,6 +455,26 @@
     return f->clazz->_fh_read(f, buf, len);
 }
 
+int adb_pread(borrowed_fd fd, void* buf, int len, off64_t offset) {
+    OVERLAPPED overlapped = {};
+    overlapped.Offset = static_cast<DWORD>(offset);
+    overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
+    DWORD bytes_read;
+    if (!::ReadFile(adb_get_os_handle(fd), buf, static_cast<DWORD>(len), &bytes_read,
+                    &overlapped)) {
+        D("adb_pread: could not read %d bytes from FD %d", len, fd.get());
+        switch (::GetLastError()) {
+            case ERROR_IO_PENDING:
+                errno = EAGAIN;
+                return -1;
+            default:
+                errno = EINVAL;
+                return -1;
+        }
+    }
+    return static_cast<int>(bytes_read);
+}
+
 int adb_write(borrowed_fd fd, const void* buf, int len) {
     FH f = _fh_from_int(fd, __func__);
 
@@ -478,6 +497,25 @@
     return f->clazz->_fh_writev(f, iov, iovcnt);
 }
 
+int adb_pwrite(borrowed_fd fd, const void* buf, int len, off64_t offset) {
+    OVERLAPPED params = {};
+    params.Offset = static_cast<DWORD>(offset);
+    params.OffsetHigh = static_cast<DWORD>(offset >> 32);
+    DWORD bytes_written = 0;
+    if (!::WriteFile(adb_get_os_handle(fd), buf, len, &bytes_written, &params)) {
+        D("adb_pwrite: could not write %d bytes to FD %d", len, fd.get());
+        switch (::GetLastError()) {
+            case ERROR_IO_PENDING:
+                errno = EAGAIN;
+                return -1;
+            default:
+                errno = EINVAL;
+                return -1;
+        }
+    }
+    return static_cast<int>(bytes_written);
+}
+
 int64_t adb_lseek(borrowed_fd fd, int64_t pos, int where) {
     FH f = _fh_from_int(fd, __func__);
     if (!f) {
@@ -500,6 +538,20 @@
     return 0;
 }
 
+HANDLE adb_get_os_handle(borrowed_fd fd) {
+    FH f = _fh_from_int(fd, __func__);
+
+    if (!f) {
+        errno = EBADF;
+        return nullptr;
+    }
+
+    D("adb_get_os_handle: %s", f->name);
+    const intptr_t intptr_handle = f->clazz->_fh_get_os_handle(f);
+    const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+    return handle;
+}
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -610,15 +662,6 @@
 
 static int _fh_socket_close(FH f) {
     if (f->fh_socket != INVALID_SOCKET) {
-        /* gently tell any peer that we're closing the socket */
-        if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
-            // If the socket is not connected, this returns an error. We want to
-            // minimize logging spam, so don't log these errors for now.
-#if 0
-            D("socket shutdown failed: %s",
-              android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
-#endif
-        }
         if (closesocket(f->fh_socket) == SOCKET_ERROR) {
             // Don't set errno here, since adb_close will ignore it.
             const DWORD err = WSAGetLastError();
@@ -697,12 +740,16 @@
               android::base::SystemErrorCodeToString(err).c_str());
         }
         _socket_set_errno(err);
-        result = -1;
+        return -1;
     }
     CHECK_GE(static_cast<DWORD>(std::numeric_limits<int>::max()), bytes_written);
     return static_cast<int>(bytes_written);
 }
 
+static intptr_t _fh_socket_get_os_handle(FH f) {
+    return f->u.socket;
+}
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 841865a..d9749ac 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -49,7 +49,7 @@
 #include "adb_io.h"
 #include "adb_trace.h"
 #include "adb_utils.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
 #include "sysdeps/chrono.h"
 
 using android::base::ScopedLockAssertion;
@@ -73,6 +73,7 @@
 const char* const kFeatureAbb = "abb";
 const char* const kFeatureFixedPushSymlinkTimestamp = "fixed_push_symlink_timestamp";
 const char* const kFeatureAbbExec = "abb_exec";
+const char* const kFeatureRemountShell = "remount_shell";
 
 namespace {
 
@@ -542,9 +543,7 @@
     // for the first time, even if no update occurred.
     if (tracker->update_needed) {
         tracker->update_needed = false;
-
-        std::string transports = list_transports(tracker->long_output);
-        device_tracker_send(tracker, transports);
+        device_tracker_send(tracker, list_transports(tracker->long_output));
     }
 }
 
@@ -587,13 +586,11 @@
     update_transport_status();
 
     // Notify `adb track-devices` clients.
-    std::string transports = list_transports(false);
-
     device_tracker* tracker = device_tracker_list;
     while (tracker != nullptr) {
         device_tracker* next = tracker->next;
         // This may destroy the tracker if the connection is closed.
-        device_tracker_send(tracker, transports);
+        device_tracker_send(tracker, list_transports(tracker->long_output));
         tracker = next;
     }
 }
@@ -1053,6 +1050,7 @@
             kFeatureAbb,
             kFeatureFixedPushSymlinkTimestamp,
             kFeatureAbbExec,
+            kFeatureRemountShell,
             // Increment ADB_SERVER_VERSION when adding a feature that adbd needs
             // to know about. Otherwise, the client can be stuck running an old
             // version of the server even after upgrading their copy of adb.
diff --git a/adb/transport.h b/adb/transport.h
index f4490ed..245037e 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -69,6 +69,7 @@
 extern const char* const kFeatureAbb;
 // adbd properly updates symlink timestamps on push.
 extern const char* const kFeatureFixedPushSymlinkTimestamp;
+extern const char* const kFeatureRemountShell;
 
 TransportId NextTransportId();
 
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index b66f8fa..00beb3a 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -19,7 +19,7 @@
 #include <gtest/gtest.h>
 
 #include "adb.h"
-#include "fdevent_test.h"
+#include "fdevent/fdevent_test.h"
 
 struct TransportTest : public FdeventTest {};
 
diff --git a/adb/types.h b/adb/types.h
index cd1366d..8bd66be 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <string.h>
+
 #include <algorithm>
 #include <deque>
 #include <memory>
diff --git a/base/Android.bp b/base/Android.bp
index 58b6fb5..357ce01 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -53,6 +53,7 @@
         "logging.cpp",
         "mapped_file.cpp",
         "parsenetaddress.cpp",
+        "process.cpp",
         "properties.cpp",
         "quick_exit.cpp",
         "stringprintf.cpp",
@@ -145,6 +146,7 @@
         "parsedouble_test.cpp",
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
+        "process_test.cpp",
         "properties_test.cpp",
         "quick_exit_test.cpp",
         "result_test.cpp",
diff --git a/base/errors_unix.cpp b/base/errors_unix.cpp
index 296995e..48269b6 100644
--- a/base/errors_unix.cpp
+++ b/base/errors_unix.cpp
@@ -17,6 +17,7 @@
 #include "android-base/errors.h"
 
 #include <errno.h>
+#include <string.h>
 
 namespace android {
 namespace base {
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index cbbd8c9..2d0f614 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -18,6 +18,9 @@
 
 /* A cross-platform equivalent of bionic's <sys/endian.h>. */
 
+/* For __BIONIC__ and __GLIBC__ */
+#include <sys/cdefs.h>
+
 #if defined(__BIONIC__)
 
 #include <sys/endian.h>
@@ -38,26 +41,32 @@
 #define betoh16(x) be16toh(x)
 #define betoh32(x) be32toh(x)
 #define betoh64(x) be64toh(x)
+#define letoh16(x) le16toh(x)
+#define letoh32(x) le32toh(x)
+#define letoh64(x) le64toh(x)
 
 #else
 
-/* Mac OS and Windows have nothing. */
-
-#define __LITTLE_ENDIAN 1234
-#define LITTLE_ENDIAN __LITTLE_ENDIAN
-
-#define __BIG_ENDIAN 4321
-#define BIG_ENDIAN __BIG_ENDIAN
-
-#define __BYTE_ORDER __LITTLE_ENDIAN
-#define BYTE_ORDER __BYTE_ORDER
-
+#if defined(__APPLE__)
+/* macOS has some of the basics. */
+#include <sys/_endian.h>
+#else
+/* Windows has even less. */
+#include <sys/param.h>
 #define htons(x) __builtin_bswap16(x)
 #define htonl(x) __builtin_bswap32(x)
-#define htonq(x) __builtin_bswap64(x)
-
 #define ntohs(x) __builtin_bswap16(x)
 #define ntohl(x) __builtin_bswap32(x)
+#endif
+
+/* Neither macOS nor Windows have the rest. */
+
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#define htonq(x) __builtin_bswap64(x)
+
 #define ntohq(x) __builtin_bswap64(x)
 
 #define htobe16(x) __builtin_bswap16(x)
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 957a8a0..030ef35e 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -81,15 +81,6 @@
 #define _NODISCARD_
 #endif
 
-namespace {
-template< class T >
-struct remove_cvref {
-  typedef std::remove_cv_t<std::remove_reference_t<T>> type;
-};
-template< class T >
-using remove_cvref_t = typename remove_cvref<T>::type;
-} // namespace
-
 // Class expected
 template<class T, class E>
 class _NODISCARD_ expected {
@@ -182,25 +173,23 @@
     else var_ = unexpected(std::move(rhs.error()));
   }
 
-  template<class U = T _ENABLE_IF(
-    std::is_constructible_v<T, U&&> &&
-    !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
-    !std::is_same_v<expected<T, E>, remove_cvref_t<U>> &&
-    !std::is_same_v<unexpected<E>, remove_cvref_t<U>> &&
-    std::is_convertible_v<U&&,T> /* non-explicit */
-  )>
-  constexpr expected(U&& v)
-  : var_(std::in_place_index<0>, std::forward<U>(v)) {}
+  template <class U = T _ENABLE_IF(
+                std::is_constructible_v<T, U&&> &&
+                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
+                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+                !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+                std::is_convertible_v<U&&, T> /* non-explicit */
+                )>
+  constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
 
-  template<class U = T _ENABLE_IF(
-    std::is_constructible_v<T, U&&> &&
-    !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
-    !std::is_same_v<expected<T, E>, remove_cvref_t<U>> &&
-    !std::is_same_v<unexpected<E>, remove_cvref_t<U>> &&
-    !std::is_convertible_v<U&&,T> /* explicit */
-  )>
-  constexpr explicit expected(U&& v)
-  : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
+  template <class U = T _ENABLE_IF(
+                std::is_constructible_v<T, U&&> &&
+                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
+                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+                !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+                !std::is_convertible_v<U&&, T> /* explicit */
+                )>
+  constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
 
   template<class G = E _ENABLE_IF(
     std::is_constructible_v<E, const G&> &&
@@ -267,16 +256,15 @@
   expected& operator=(const expected& rhs) = default;
 
   // Note for SFNAIE above applies to here as well
-  expected& operator=(expected&& rhs) = default;
+  expected& operator=(expected&& rhs) noexcept(
+      std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
 
-  template<class U = T _ENABLE_IF(
-    !std::is_void_v<T> &&
-    !std::is_same_v<expected<T,E>, remove_cvref_t<U>> &&
-    !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
-    std::is_constructible_v<T,U> &&
-    std::is_assignable_v<T&,U> &&
-    std::is_nothrow_move_constructible_v<E>
-  )>
+  template <class U = T _ENABLE_IF(
+                !std::is_void_v<T> &&
+                !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+                !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
+                std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
+                std::is_nothrow_move_constructible_v<E>)>
   expected& operator=(U&& rhs) {
     var_ = T(std::forward<U>(rhs));
     return *this;
@@ -555,7 +543,7 @@
   expected& operator=(const expected& rhs) = default;
 
   // Note for SFNAIE above applies to here as well
-  expected& operator=(expected&& rhs) = default;
+  expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
 
   template<class G = E>
   expected& operator=(const unexpected<G>& rhs) {
@@ -646,15 +634,13 @@
  public:
   // constructors
   constexpr unexpected(const unexpected&) = default;
-  constexpr unexpected(unexpected&&) = default;
+  constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
 
-  template<class Err = E _ENABLE_IF(
-    std::is_constructible_v<E, Err> &&
-    !std::is_same_v<remove_cvref_t<E>, std::in_place_t> &&
-    !std::is_same_v<remove_cvref_t<E>, unexpected>
-  )>
-  constexpr unexpected(Err&& e)
-  : val_(std::forward<Err>(e)) {}
+  template <class Err = E _ENABLE_IF(
+                std::is_constructible_v<E, Err> &&
+                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
+                !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
+  constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
 
   template<class U, class... Args _ENABLE_IF(
     std::is_constructible_v<E, std::initializer_list<U>&, Args...>
@@ -724,7 +710,8 @@
 
   // assignment
   constexpr unexpected& operator=(const unexpected&) = default;
-  constexpr unexpected& operator=(unexpected&&) = default;
+  constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
+      default;
   template<class Err = E>
   constexpr unexpected& operator=(const unexpected<Err>& rhs) {
     val_ = rhs.value();
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index f94cc25..ab6476c 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -469,7 +469,7 @@
 }  // namespace base
 }  // namespace android
 
-namespace std {
+namespace std {  // NOLINT(cert-dcl58-cpp)
 
 // Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
 //
diff --git a/base/include/android-base/mapped_file.h b/base/include/android-base/mapped_file.h
index 2ab49ab..6a19f1b 100644
--- a/base/include/android-base/mapped_file.h
+++ b/base/include/android-base/mapped_file.h
@@ -28,15 +28,17 @@
 #include <windows.h>
 #define PROT_READ 1
 #define PROT_WRITE 2
+using os_handle = HANDLE;
 #else
 #include <sys/mman.h>
+using os_handle = int;
 #endif
 
 namespace android {
 namespace base {
 
 /**
- * A region of a file mapped into memory, also known as MmapFile.
+ * A region of a file mapped into memory (for grepping: also known as MmapFile or file mapping).
  */
 class MappedFile {
  public:
@@ -49,16 +51,33 @@
                                             int prot);
 
   /**
+   * Same thing, but using the raw OS file handle instead of a CRT wrapper.
+   */
+  static MappedFile FromOsHandle(os_handle h, off64_t offset, size_t length, int prot);
+
+  /**
    * Removes the mapping.
    */
   ~MappedFile();
 
-  char* data() { return base_ + offset_; }
-  size_t size() { return size_; }
+  /**
+   * Not copyable but movable.
+   */
+  MappedFile(MappedFile&& other);
+  MappedFile& operator=(MappedFile&& other);
+
+  char* data() const { return base_ + offset_; }
+  size_t size() const { return size_; }
+
+  bool isValid() const { return base_ != nullptr; }
+
+  explicit operator bool() const { return isValid(); }
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(MappedFile);
 
+  void Close();
+
   char* base_;
   size_t size_;
 
diff --git a/base/include/android-base/process.h b/base/include/android-base/process.h
new file mode 100644
index 0000000..69ed3fb
--- /dev/null
+++ b/base/include/android-base/process.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <sys/types.h>
+
+#include <iterator>
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace base {
+
+class AllPids {
+  class PidIterator {
+   public:
+    PidIterator(DIR* dir) : dir_(dir, closedir) { Increment(); }
+    PidIterator& operator++() {
+      Increment();
+      return *this;
+    }
+    bool operator==(const PidIterator& other) const { return pid_ == other.pid_; }
+    bool operator!=(const PidIterator& other) const { return !(*this == other); }
+    long operator*() const { return pid_; }
+    // iterator traits
+    using difference_type = pid_t;
+    using value_type = pid_t;
+    using pointer = const pid_t*;
+    using reference = const pid_t&;
+    using iterator_category = std::input_iterator_tag;
+
+   private:
+    void Increment();
+
+    pid_t pid_ = -1;
+    std::unique_ptr<DIR, decltype(&closedir)> dir_;
+  };
+
+ public:
+  PidIterator begin() { return opendir("/proc"); }
+  PidIterator end() { return nullptr; }
+};
+
+}  // namespace base
+}  // namespace android
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 3a02cff..6e11b4e 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -92,6 +92,8 @@
   explicit unique_fd_impl(int fd) { reset(fd); }
   ~unique_fd_impl() { reset(); }
 
+  unique_fd_impl(const unique_fd_impl&) = delete;
+  void operator=(const unique_fd_impl&) = delete;
   unique_fd_impl(unique_fd_impl&& other) noexcept { reset(other.release()); }
   unique_fd_impl& operator=(unique_fd_impl&& s) noexcept {
     int fd = s.fd_;
@@ -118,6 +120,8 @@
   // Catch bogus error checks (i.e.: "!fd" instead of "fd != -1").
   bool operator!() const = delete;
 
+  bool ok() const { return get() != -1; }
+
   int release() __attribute__((warn_unused_result)) {
     tag(fd_, this, nullptr);
     int ret = fd_;
@@ -167,9 +171,6 @@
   static auto close(int fd, void*) -> decltype(T::Close(fd), void()) {
     T::Close(fd);
   }
-
-  unique_fd_impl(const unique_fd_impl&);
-  void operator=(const unique_fd_impl&);
 };
 
 using unique_fd = unique_fd_impl<DefaultCloser>;
@@ -258,9 +259,9 @@
 
 // A wrapper type that can be implicitly constructed from either int or unique_fd.
 struct borrowed_fd {
-  /* implicit */ borrowed_fd(int fd) : fd_(fd) {}
+  /* implicit */ borrowed_fd(int fd) : fd_(fd) {}  // NOLINT
   template <typename T>
-  /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {}
+  /* implicit */ borrowed_fd(const unique_fd_impl<T>& ufd) : fd_(ufd.get()) {}  // NOLINT
 
   int get() const { return fd_; }
 
diff --git a/base/mapped_file.cpp b/base/mapped_file.cpp
index f60de56..862b73b 100644
--- a/base/mapped_file.cpp
+++ b/base/mapped_file.cpp
@@ -16,13 +16,15 @@
 
 #include "android-base/mapped_file.h"
 
-#include <errno.h>
+#include <utility>
 
-#include "android-base/unique_fd.h"
+#include <errno.h>
 
 namespace android {
 namespace base {
 
+static constexpr char kEmptyBuffer[] = {'0'};
+
 static off64_t InitPageSize() {
 #if defined(_WIN32)
   SYSTEM_INFO si;
@@ -35,51 +37,86 @@
 
 std::unique_ptr<MappedFile> MappedFile::FromFd(borrowed_fd fd, off64_t offset, size_t length,
                                                int prot) {
-  static off64_t page_size = InitPageSize();
+#if defined(_WIN32)
+  auto file =
+      FromOsHandle(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), offset, length, prot);
+#else
+  auto file = FromOsHandle(fd.get(), offset, length, prot);
+#endif
+  return file ? std::make_unique<MappedFile>(std::move(file)) : std::unique_ptr<MappedFile>{};
+}
+
+MappedFile MappedFile::FromOsHandle(os_handle h, off64_t offset, size_t length, int prot) {
+  static const off64_t page_size = InitPageSize();
   size_t slop = offset % page_size;
   off64_t file_offset = offset - slop;
   off64_t file_length = length + slop;
 
 #if defined(_WIN32)
-  HANDLE handle =
-      CreateFileMapping(reinterpret_cast<HANDLE>(_get_osfhandle(fd.get())), nullptr,
-                        (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
+  HANDLE handle = CreateFileMappingW(
+      h, nullptr, (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, 0, 0, nullptr);
   if (handle == nullptr) {
     // http://b/119818070 "app crashes when reading asset of zero length".
     // Return a MappedFile that's only valid for reading the size.
-    if (length == 0) {
-      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0, nullptr});
+    if (length == 0 && ::GetLastError() == ERROR_FILE_INVALID) {
+      return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0, nullptr};
     }
-    return nullptr;
+    return MappedFile(nullptr, 0, 0, nullptr);
   }
   void* base = MapViewOfFile(handle, (prot & PROT_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ, 0,
                              file_offset, file_length);
   if (base == nullptr) {
     CloseHandle(handle);
-    return nullptr;
+    return MappedFile(nullptr, 0, 0, nullptr);
   }
-  return std::unique_ptr<MappedFile>(
-      new MappedFile{static_cast<char*>(base), length, slop, handle});
+  return MappedFile{static_cast<char*>(base), length, slop, handle};
 #else
-  void* base = mmap(nullptr, file_length, prot, MAP_SHARED, fd.get(), file_offset);
+  void* base = mmap(nullptr, file_length, prot, MAP_SHARED, h, file_offset);
   if (base == MAP_FAILED) {
     // http://b/119818070 "app crashes when reading asset of zero length".
     // mmap fails with EINVAL for a zero length region.
     if (errno == EINVAL && length == 0) {
-      return std::unique_ptr<MappedFile>(new MappedFile{nullptr, 0, 0});
+      return MappedFile{const_cast<char*>(kEmptyBuffer), 0, 0};
     }
-    return nullptr;
+    return MappedFile(nullptr, 0, 0);
   }
-  return std::unique_ptr<MappedFile>(new MappedFile{static_cast<char*>(base), length, slop});
+  return MappedFile{static_cast<char*>(base), length, slop};
 #endif
 }
 
+MappedFile::MappedFile(MappedFile&& other)
+    : base_(std::exchange(other.base_, nullptr)),
+      size_(std::exchange(other.size_, 0)),
+      offset_(std::exchange(other.offset_, 0))
+#ifdef _WIN32
+      ,
+      handle_(std::exchange(other.handle_, nullptr))
+#endif
+{
+}
+
+MappedFile& MappedFile::operator=(MappedFile&& other) {
+  Close();
+  base_ = std::exchange(other.base_, nullptr);
+  size_ = std::exchange(other.size_, 0);
+  offset_ = std::exchange(other.offset_, 0);
+#ifdef _WIN32
+  handle_ = std::exchange(other.handle_, nullptr);
+#endif
+  return *this;
+}
+
 MappedFile::~MappedFile() {
+  Close();
+}
+
+void MappedFile::Close() {
 #if defined(_WIN32)
-  if (base_ != nullptr) UnmapViewOfFile(base_);
+  if (base_ != nullptr && size_ != 0) UnmapViewOfFile(base_);
   if (handle_ != nullptr) CloseHandle(handle_);
+  handle_ = nullptr;
 #else
-  if (base_ != nullptr) munmap(base_, size_ + offset_);
+  if (base_ != nullptr && size_ != 0) munmap(base_, size_ + offset_);
 #endif
 
   base_ = nullptr;
diff --git a/base/mapped_file_test.cpp b/base/mapped_file_test.cpp
index cfde73c..3629108 100644
--- a/base/mapped_file_test.cpp
+++ b/base/mapped_file_test.cpp
@@ -44,5 +44,8 @@
   ASSERT_TRUE(tf.fd != -1);
 
   auto m = android::base::MappedFile::FromFd(tf.fd, 4096, 0, PROT_READ);
-  ASSERT_EQ(0u, m->size());
+  ASSERT_NE(nullptr, m);
+  EXPECT_TRUE((bool)*m);
+  EXPECT_EQ(0u, m->size());
+  EXPECT_NE(nullptr, m->data());
 }
diff --git a/fs_mgr/libfiemap_writer/utility.h b/base/process.cpp
similarity index 66%
copy from fs_mgr/libfiemap_writer/utility.h
copy to base/process.cpp
index 2d418da..b8cabf6 100644
--- a/fs_mgr/libfiemap_writer/utility.h
+++ b/base/process.cpp
@@ -14,17 +14,26 @@
  * limitations under the License.
  */
 
-#pragma once
-
-#include <string>
+#include "android-base/process.h"
 
 namespace android {
-namespace fiemap_writer {
+namespace base {
 
-// Given a file that will be created, determine the maximum size its containing
-// filesystem allows. Note this is a theoretical maximum size; free space is
-// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
+void AllPids::PidIterator::Increment() {
+  if (!dir_) {
+    return;
+  }
 
-}  // namespace fiemap_writer
+  dirent* de;
+  while ((de = readdir(dir_.get())) != nullptr) {
+    pid_t pid = atoi(de->d_name);
+    if (pid != 0) {
+      pid_ = pid;
+      return;
+    }
+  }
+  pid_ = -1;
+}
+
+}  // namespace base
 }  // namespace android
diff --git a/fs_mgr/libfiemap_writer/utility.h b/base/process_test.cpp
similarity index 62%
copy from fs_mgr/libfiemap_writer/utility.h
copy to base/process_test.cpp
index 2d418da..056f667 100644
--- a/fs_mgr/libfiemap_writer/utility.h
+++ b/base/process_test.cpp
@@ -14,17 +14,22 @@
  * limitations under the License.
  */
 
-#pragma once
+#include "android-base/process.h"
 
-#include <string>
+#include <unistd.h>
 
-namespace android {
-namespace fiemap_writer {
+#include <gtest/gtest.h>
 
-// Given a file that will be created, determine the maximum size its containing
-// filesystem allows. Note this is a theoretical maximum size; free space is
-// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
+TEST(process, find_ourselves) {
+#if defined(__linux__)
+  bool found_our_pid = false;
+  for (const auto& pid : android::base::AllPids{}) {
+    if (pid == getpid()) {
+      found_our_pid = true;
+    }
+  }
 
-}  // namespace fiemap_writer
-}  // namespace android
+  EXPECT_TRUE(found_our_pid);
+
+#endif
+}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 6936cc2..cd9fda3 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -140,13 +140,13 @@
     {"mba_err", 13},
     {"Watchdog", 14},
     {"Panic", 15},
-    {"power_key", 16},  // Mediatek
-    {"power_on", 17},
+    {"power_key", 16},  // aliasReasons to cold,powerkey (Mediatek)
+    {"power_on", 17},   // aliasReasons to cold,powerkey
     {"Reboot", 18},
     {"rtc", 19},
     {"edl", 20},
     {"oem_pon1", 21},
-    {"oem_powerkey", 22},
+    {"oem_powerkey", 22},  // aliasReasons to cold,powerkey
     {"oem_unknown_reset", 23},
     {"srto: HWWDT reset SC", 24},
     {"srto: HWWDT reset platform", 25},
@@ -201,15 +201,15 @@
     {"hard,hw_reset", 72},
     {"shutdown,suspend", 73},    // Suspend to RAM
     {"shutdown,hibernate", 74},  // Suspend to DISK
-    {"power_on_key", 75},
-    {"reboot_by_key", 76},
-    {"wdt_by_pass_pwk", 77},  // Mediatek
-    {"reboot_longkey", 78},
-    {"powerkey", 79},
-    {"usb", 80},               // Mediatek
-    {"wdt", 81},               // Mediatek
-    {"tool_by_pass_pwk", 82},  // Mediatek
-    {"2sec_reboot", 83},       // Mediatek
+    {"power_on_key", 75},        // aliasReasons to cold,powerkey
+    {"reboot_by_key", 76},       // translated to reboot,by_key
+    {"wdt_by_pass_pwk", 77},     // Mediatek
+    {"reboot_longkey", 78},      // translated to reboot,longkey
+    {"powerkey", 79},            // aliasReasons to cold,powerkey
+    {"usb", 80},                 // aliasReasons to cold,charger (Mediatek)
+    {"wdt", 81},                 // Mediatek
+    {"tool_by_pass_pwk", 82},    // aliasReasons to reboot,tool (Mediatek)
+    {"2sec_reboot", 83},         // aliasReasons to cold,rtc,2sec (Mediatek)
     {"reboot,by_key", 84},
     {"reboot,longkey", 85},
     {"reboot,2sec", 86},  // Deprecate in two years, replaced with cold,rtc,2sec
@@ -219,28 +219,28 @@
     {"reboot,rescueparty", 90},
     {"charge", 91},
     {"oem_tz_crash", 92},
-    {"uvlo", 93},  // aliasReasons converts to reboot,undervoltage
+    {"uvlo", 93},  // aliasReasons to reboot,undervoltage
     {"oem_ps_hold", 94},
     {"abnormal_reset", 95},
     {"oemerr_unknown", 96},
     {"reboot_fastboot_mode", 97},
     {"watchdog_apps_bite", 98},
     {"xpu_err", 99},
-    {"power_on_usb", 100},
+    {"power_on_usb", 100},  // aliasReasons to cold,charger
     {"watchdog_rpm", 101},
     {"watchdog_nonsec", 102},
     {"watchdog_apps_bark", 103},
     {"reboot_dmverity_corrupted", 104},
-    {"reboot_smpl", 105},  // aliasReasons converts to reboot,powerloss
+    {"reboot_smpl", 105},  // aliasReasons to reboot,powerloss
     {"watchdog_sdi_apps_reset", 106},
-    {"smpl", 107},  // aliasReasons converts to reboot,powerloss
+    {"smpl", 107},  // aliasReasons to reboot,powerloss
     {"oem_modem_failed_to_powerup", 108},
     {"reboot_normal", 109},
     {"oem_lpass_cfg", 110},
     {"oem_xpu_ns_error", 111},
-    {"power_key_press", 112},
+    {"power_key_press", 112},  // aliasReasons to cold,powerkey
     {"hardware_reset", 113},
-    {"reboot_by_powerkey", 114},
+    {"reboot_by_powerkey", 114},  // aliasReasons to cold,powerkey (is this correct?)
     {"reboot_verity", 115},
     {"oem_rpm_undef_error", 116},
     {"oem_crash_on_the_lk", 117},
@@ -250,7 +250,7 @@
     {"factory_cable", 121},
     {"oem_ar6320_failed_to_powerup", 122},
     {"watchdog_rpm_bite", 123},
-    {"power_on_cable", 124},
+    {"power_on_cable", 124},  // aliasReasons to cold,charger
     {"reboot_unknown", 125},
     {"wireless_charger", 126},
     {"0x776655ff", 127},
@@ -839,12 +839,12 @@
     // following table smaller.
     static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
         {"watchdog", "wdog"},
-        {"cold,powerkey", "powerkey|power_key|PowerKey"},
         {"kernel_panic", "panic"},
         {"shutdown,thermal", "thermal"},
         {"warm,s3_wakeup", "s3_wakeup"},
         {"hard,hw_reset", "hw_reset"},
-        {"cold,charger", "usb"},
+        {"cold,charger", "usb|power_on_cable"},
+        {"cold,powerkey", "powerkey|power_key|PowerKey|power_on"},
         {"cold,rtc", "rtc"},
         {"cold,rtc,2sec", "2sec_reboot"},
         {"!warm", "wdt_by_pass_pwk"},  // change flavour of blunt
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 60eb241..1a5b435 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -22,9 +22,11 @@
 #include <sys/poll.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <chrono>
+#include <iomanip>
 
 #include <android-base/cmsg.h>
 #include <android-base/file.h>
@@ -42,8 +44,10 @@
 
 using namespace std::chrono_literals;
 
+using android::base::ReadFileToString;
 using android::base::SendFileDescriptors;
 using android::base::unique_fd;
+using android::base::WriteStringToFd;
 
 static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
   const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL;
@@ -65,6 +69,76 @@
   tv->tv_usec = static_cast<long>(microseconds.count());
 }
 
+static void get_wchan_header(pid_t pid, std::stringstream& buffer) {
+  struct tm now;
+  time_t t = time(nullptr);
+  localtime_r(&t, &now);
+  char timestamp[32];
+  strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &now);
+  std::string time_now(timestamp);
+
+  std::string path = "/proc/" + std::to_string(pid) + "/cmdline";
+
+  char proc_name_buf[1024];
+  const char* proc_name = nullptr;
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path.c_str(), "r"), &fclose);
+
+  if (fp) {
+    proc_name = fgets(proc_name_buf, sizeof(proc_name_buf), fp.get());
+  }
+
+  if (!proc_name) {
+    proc_name = "<unknown>";
+  }
+
+  buffer << "\n----- Waiting Channels: pid " << pid << " at " << time_now << " -----\n"
+         << "Cmd line: " << proc_name << "\n";
+}
+
+static void get_wchan_footer(pid_t pid, std::stringstream& buffer) {
+  buffer << "----- end " << std::to_string(pid) << " -----\n";
+}
+
+/**
+ * Returns the wchan data for each thread in the process,
+ * or empty string if unable to obtain any data.
+ */
+static std::string get_wchan_data(pid_t pid) {
+  std::stringstream buffer;
+  std::vector<pid_t> tids;
+
+  if (!android::procinfo::GetProcessTids(pid, &tids)) {
+    LOG(WARNING) << "libdebuggerd_client: Failed to get process tids";
+    return buffer.str();
+  }
+
+  std::stringstream data;
+  for (int tid : tids) {
+    std::string path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/wchan";
+    std::string wchan_str;
+    if (!ReadFileToString(path, &wchan_str, true)) {
+      PLOG(WARNING) << "libdebuggerd_client: Failed to read \"" << path << "\"";
+      continue;
+    }
+    data << "sysTid=" << std::left << std::setw(10) << tid << wchan_str << "\n";
+  }
+
+  if (std::string str = data.str(); !str.empty()) {
+    get_wchan_header(pid, buffer);
+    buffer << "\n" << str << "\n";
+    get_wchan_footer(pid, buffer);
+    buffer << "\n";
+  }
+
+  return buffer.str();
+}
+
+static void dump_wchan_data(const std::string& data, int fd, pid_t pid) {
+  if (!WriteStringToFd(data, fd)) {
+    LOG(WARNING) << "libdebuggerd_client: Failed to dump wchan data for pid: " << pid;
+  }
+}
+
 bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
                             unique_fd output_fd) {
   pid_t pid = tid;
@@ -261,6 +335,17 @@
   if (copy == -1) {
     return -1;
   }
+
+  // debuggerd_trigger_dump results in every thread in the process being interrupted
+  // by a signal, so we need to fetch the wchan data before calling that.
+  std::string wchan_data = get_wchan_data(tid);
+
   int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
-  return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
+  int ret = debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
+
+  // Dump wchan data, since only privileged processes (CAP_SYS_ADMIN) can read
+  // kernel stack traces (/proc/*/stack).
+  dump_wchan_data(wchan_data, fd, tid);
+
+  return ret;
 }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 1f0e420..fbc8b97 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1099,3 +1099,30 @@
   // This should be good enough, though...
   ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?";
 }
+
+static __attribute__((__noinline__)) void overflow_stack(void* p) {
+  void* buf[1];
+  buf[0] = p;
+  static volatile void* global = buf;
+  if (global) {
+    global = buf;
+    overflow_stack(&buf);
+  }
+}
+
+TEST_F(CrasherTest, stack_overflow) {
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([]() { overflow_stack(nullptr); });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
+}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index d246722..da2ba58 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -86,7 +86,39 @@
   _LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
 }
 
-static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps) {
+static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
+                                            unwindstack::Maps* maps) {
+  static constexpr uint64_t kMaxDifferenceBytes = 256;
+  uint64_t difference;
+  if (sp >= fault_addr) {
+    difference = sp - fault_addr;
+  } else {
+    difference = fault_addr - sp;
+  }
+  if (difference <= kMaxDifferenceBytes) {
+    // The faulting address is close to the current sp, check if the sp
+    // indicates a stack overflow.
+    // On arm, the sp does not get updated when the instruction faults.
+    // In this case, the sp will still be in a valid map, which is the
+    // last case below.
+    // On aarch64, the sp does get updated when the instruction faults.
+    // In this case, the sp will be in either an invalid map if triggered
+    // on the main thread, or in a guard map if in another thread, which
+    // will be the first case or second case from below.
+    unwindstack::MapInfo* map_info = maps->Find(sp);
+    if (map_info == nullptr) {
+      return "stack pointer is in a non-existent map; likely due to stack overflow.";
+    } else if ((map_info->flags & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
+      return "stack pointer is not in a rw map; likely due to stack overflow.";
+    } else if ((sp - map_info->start) <= kMaxDifferenceBytes) {
+      return "stack pointer is close to top of stack; likely stack overflow.";
+    }
+  }
+  return "";
+}
+
+static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps,
+                                unwindstack::Regs* regs) {
   std::string cause;
   if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
     if (si->si_addr < reinterpret_cast<void*>(4096)) {
@@ -101,11 +133,16 @@
       cause = "call to kuser_memory_barrier";
     } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
       cause = "call to kuser_cmpxchg64";
+    } else {
+      cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps);
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
-    unwindstack::MapInfo* map_info = maps->Find(reinterpret_cast<uint64_t>(si->si_addr));
+    uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
+    unwindstack::MapInfo* map_info = maps->Find(fault_addr);
     if (map_info != nullptr && map_info->flags == PROT_EXEC) {
       cause = "execute-only (no-read) memory access error; likely due to data in .text.";
+    } else {
+      cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps);
     }
   } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
     cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
@@ -447,7 +484,7 @@
 
   if (thread_info.siginfo) {
     dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
-    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps());
+    dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps(), thread_info.registers.get());
   }
 
   if (primary_thread) {
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index 53ef01c..b4a1e71 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -2,10 +2,10 @@
     user tombstoned
     group system
 
-    # Don't start tombstoned until after the real /data is mounted.
-    class late_start
-
     socket tombstoned_crash seqpacket 0666 system system
     socket tombstoned_intercept seqpacket 0666 system system
     socket tombstoned_java_trace seqpacket 0666 system system
     writepid /dev/cpuset/system-background/tasks
+
+on post-fs-data
+    start tombstoned
diff --git a/demangle/Android.bp b/demangle/Android.bp
deleted file mode 100644
index fd79cf8..0000000
--- a/demangle/Android.bp
+++ /dev/null
@@ -1,87 +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: "libdemangle_defaults",
-
-    host_supported: true,
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-    ],
-
-    target: {
-        linux_bionic: {
-            enabled: true,
-        },
-    },
-}
-
-cc_library {
-    name: "libdemangle",
-    defaults: ["libdemangle_defaults"],
-    vendor_available: true,
-    recovery_available: true,
-
-    srcs: [
-        "Demangler.cpp",
-    ],
-
-    local_include_dirs: [
-        "include",
-    ],
-
-    export_include_dirs: [
-        "include",
-    ],
-}
-
-cc_binary {
-    name: "demangle",
-    defaults: ["libdemangle_defaults"],
-    srcs: ["demangle.cpp"],
-    host_supported: true,
-
-    shared_libs: ["libdemangle"],
-}
-
-//-------------------------------------------------------------------------
-// Unit Tests
-//-------------------------------------------------------------------------
-cc_test {
-    name: "libdemangle_test",
-    defaults: ["libdemangle_defaults"],
-
-    srcs: [
-        "DemangleTest.cpp",
-    ],
-
-    cflags: [
-        "-O0",
-        "-g",
-    ],
-
-    shared_libs: [
-        "libdemangle",
-    ],
-
-    test_suites: ["device-tests"],
-    required: [
-        "libdemangle",
-    ],
-}
diff --git a/demangle/Android.mk b/demangle/Android.mk
deleted file mode 100644
index d8082a9..0000000
--- a/demangle/Android.mk
+++ /dev/null
@@ -1,31 +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 := demangle_fuzzer
-LOCAL_SRC_FILES := \
-    Demangler.cpp \
-    demangle_fuzzer.cpp \
-
-LOCAL_CFLAGS := \
-    -Wall \
-    -Werror \
-    -Wextra \
-
-include $(BUILD_FUZZ_TEST)
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
deleted file mode 100644
index 1787031..0000000
--- a/demangle/DemangleTest.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include <gtest/gtest.h>
-
-#include <demangle.h>
-
-#include "Demangler.h"
-
-TEST(DemangleTest, IllegalArgumentModifiers) {
-  Demangler demangler;
-
-  ASSERT_EQ("_Zpp4FUNKK", demangler.Parse("_Zpp4FUNKK"));
-  ASSERT_EQ("_Zpp4FUNVV", demangler.Parse("_Zpp4FUNVV"));
-}
-
-TEST(DemangleTest, VoidArgument) {
-  Demangler demangler;
-
-  ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
-  ASSERT_EQ("func(void&)", demangler.Parse("_ZN4funcERv"));
-  ASSERT_EQ("func(void, void)", demangler.Parse("_ZN4funcEvv"));
-  ASSERT_EQ("func(void*)", demangler.Parse("_ZN4funcEPv"));
-  ASSERT_EQ("func(void const)", demangler.Parse("_ZN4funcEKv"));
-  ASSERT_EQ("func(void volatile)", demangler.Parse("_ZN4funcEVv"));
-}
-
-TEST(DemangleTest, ArgumentModifiers) {
-  Demangler demangler;
-
-  ASSERT_EQ("func(char)", demangler.Parse("_ZN4funcEc"));
-  ASSERT_EQ("func(char*)", demangler.Parse("_ZN4funcEPc"));
-  ASSERT_EQ("func(char**)", demangler.Parse("_ZN4funcEPPc"));
-  ASSERT_EQ("func(char***)", demangler.Parse("_ZN4funcEPPPc"));
-  ASSERT_EQ("func(char&)", demangler.Parse("_ZN4funcERc"));
-  ASSERT_EQ("func(char*&)", demangler.Parse("_ZN4funcERPc"));
-  ASSERT_EQ("func(char&)", demangler.Parse("_ZN4funcERRc"));
-  ASSERT_EQ("func(char*&*)", demangler.Parse("_ZN4funcEPRPc"));
-  ASSERT_EQ("func(char**&)", demangler.Parse("_ZN4funcERRPPc"));
-  ASSERT_EQ("func(char const)", demangler.Parse("_ZN4funcEKc"));
-  ASSERT_EQ("func(char volatile)", demangler.Parse("_ZN4funcEVc"));
-  ASSERT_EQ("func(char volatile const)", demangler.Parse("_ZN4funcEKVc"));
-  ASSERT_EQ("func(char const volatile)", demangler.Parse("_ZN4funcEVKc"));
-  ASSERT_EQ("func(char const* volatile&)", demangler.Parse("_ZN4funcERVPKc"));
-  ASSERT_EQ("func(void, char, short)", demangler.Parse("_ZN4funcEvcs"));
-  ASSERT_EQ("func(void*, char&, short&*)", demangler.Parse("_ZN4funcEPvRcPRs"));
-}
-
-TEST(DemangleTest, FunctionModifiers) {
-  Demangler demangler;
-
-  ASSERT_EQ("func() const", demangler.Parse("_ZNK4funcEv"));
-  ASSERT_EQ("func() volatile", demangler.Parse("_ZNV4funcEv"));
-  ASSERT_EQ("func() volatile const", demangler.Parse("_ZNKV4funcEv"));
-  ASSERT_EQ("func() const volatile", demangler.Parse("_ZNVK4funcEv"));
-}
-
-TEST(DemangleTest, MultiplePartsInName) {
-  Demangler demangler;
-
-  ASSERT_EQ("one::two()", demangler.Parse("_ZN3one3twoEv"));
-  ASSERT_EQ("one::two::three()", demangler.Parse("_ZN3one3two5threeEv"));
-  ASSERT_EQ("one::two::three::four()", demangler.Parse("_ZN3one3two5three4fourEv"));
-  ASSERT_EQ("one::two::three::four::five()", demangler.Parse("_ZN3one3two5three4four4fiveEv"));
-  ASSERT_EQ("one(two::three::four::five)", demangler.Parse("_ZN3oneEN3two5three4four4fiveE"));
-}
-
-TEST(DemangleTest, AnonymousNamespace) {
-  Demangler demangler;
-
-  ASSERT_EQ("(anonymous namespace)::two()", demangler.Parse("_ZN12_GLOBAL__N_13twoEv"));
-  ASSERT_EQ("one::two((anonymous namespace))", demangler.Parse("_ZN3one3twoE12_GLOBAL__N_1"));
-}
-
-TEST(DemangleTest, DestructorValues) {
-  Demangler demangler;
-
-  ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD0Ev"));
-  ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD1Ev"));
-  ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD2Ev"));
-  ASSERT_EQ("one::two::~two()", demangler.Parse("_ZN3one3twoD5Ev"));
-  ASSERT_EQ("one::two::three::~three()", demangler.Parse("_ZN3one3two5threeD0Ev"));
-
-  ASSERT_EQ("_ZN3one3twoD3Ev", demangler.Parse("_ZN3one3twoD3Ev"));
-  ASSERT_EQ("_ZN3one3twoD4Ev", demangler.Parse("_ZN3one3twoD4Ev"));
-  ASSERT_EQ("_ZN3one3twoD6Ev", demangler.Parse("_ZN3one3twoD6Ev"));
-  ASSERT_EQ("_ZN3one3twoD7Ev", demangler.Parse("_ZN3one3twoD7Ev"));
-  ASSERT_EQ("_ZN3one3twoD8Ev", demangler.Parse("_ZN3one3twoD8Ev"));
-  ASSERT_EQ("_ZN3one3twoD9Ev", demangler.Parse("_ZN3one3twoD9Ev"));
-
-  ASSERT_EQ("one::two<three::four>::~two()", demangler.Parse("_ZN3one3twoIN5three4fourEED2Ev"));
-}
-
-TEST(DemangleTest, ConstructorValues) {
-  Demangler demangler;
-
-  ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC1Ev"));
-  ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC2Ev"));
-  ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC3Ev"));
-  ASSERT_EQ("one::two::two()", demangler.Parse("_ZN3one3twoC5Ev"));
-  ASSERT_EQ("one::two::three::three()", demangler.Parse("_ZN3one3two5threeC1Ev"));
-
-  ASSERT_EQ("_ZN3one3twoC0Ev", demangler.Parse("_ZN3one3twoC0Ev"));
-  ASSERT_EQ("_ZN3one3twoC4Ev", demangler.Parse("_ZN3one3twoC4Ev"));
-  ASSERT_EQ("_ZN3one3twoC6Ev", demangler.Parse("_ZN3one3twoC6Ev"));
-  ASSERT_EQ("_ZN3one3twoC7Ev", demangler.Parse("_ZN3one3twoC7Ev"));
-  ASSERT_EQ("_ZN3one3twoC8Ev", demangler.Parse("_ZN3one3twoC8Ev"));
-  ASSERT_EQ("_ZN3one3twoC9Ev", demangler.Parse("_ZN3one3twoC9Ev"));
-
-  ASSERT_EQ("one::two<three::four>::two()", demangler.Parse("_ZN3one3twoIN5three4fourEEC1Ev"));
-}
-
-TEST(DemangleTest, OperatorValues) {
-  Demangler demangler;
-
-  ASSERT_EQ("operator&&()", demangler.Parse("_Zaav"));
-  ASSERT_EQ("operator&()", demangler.Parse("_Zadv"));
-  ASSERT_EQ("operator&()", demangler.Parse("_Zanv"));
-  ASSERT_EQ("operator&=()", demangler.Parse("_ZaNv"));
-  ASSERT_EQ("operator=()", demangler.Parse("_ZaSv"));
-  ASSERT_EQ("operator()()", demangler.Parse("_Zclv"));
-  ASSERT_EQ("operator,()", demangler.Parse("_Zcmv"));
-  ASSERT_EQ("operator~()", demangler.Parse("_Zcov"));
-  ASSERT_EQ("operator delete[]()", demangler.Parse("_Zdav"));
-  ASSERT_EQ("operator*()", demangler.Parse("_Zdev"));
-  ASSERT_EQ("operator delete()", demangler.Parse("_Zdlv"));
-  ASSERT_EQ("operator/()", demangler.Parse("_Zdvv"));
-  ASSERT_EQ("operator/=()", demangler.Parse("_ZdVv"));
-  ASSERT_EQ("operator^()", demangler.Parse("_Zeov"));
-  ASSERT_EQ("operator^=()", demangler.Parse("_ZeOv"));
-  ASSERT_EQ("operator==()", demangler.Parse("_Zeqv"));
-  ASSERT_EQ("operator>=()", demangler.Parse("_Zgev"));
-  ASSERT_EQ("operator>()", demangler.Parse("_Zgtv"));
-  ASSERT_EQ("operator[]()", demangler.Parse("_Zixv"));
-  ASSERT_EQ("operator<=()", demangler.Parse("_Zlev"));
-  ASSERT_EQ("operator<<()", demangler.Parse("_Zlsv"));
-  ASSERT_EQ("operator<<=()", demangler.Parse("_ZlSv"));
-  ASSERT_EQ("operator<()", demangler.Parse("_Zltv"));
-  ASSERT_EQ("operator-()", demangler.Parse("_Zmiv"));
-  ASSERT_EQ("operator-=()", demangler.Parse("_ZmIv"));
-  ASSERT_EQ("operator*()", demangler.Parse("_Zmlv"));
-  ASSERT_EQ("operator*=()", demangler.Parse("_ZmLv"));
-  ASSERT_EQ("operator--()", demangler.Parse("_Zmmv"));
-  ASSERT_EQ("operator new[]()", demangler.Parse("_Znav"));
-  ASSERT_EQ("operator!=()", demangler.Parse("_Znev"));
-  ASSERT_EQ("operator-()", demangler.Parse("_Zngv"));
-  ASSERT_EQ("operator!()", demangler.Parse("_Zntv"));
-  ASSERT_EQ("operator new()", demangler.Parse("_Znwv"));
-  ASSERT_EQ("operator||()", demangler.Parse("_Zoov"));
-  ASSERT_EQ("operator|()", demangler.Parse("_Zorv"));
-  ASSERT_EQ("operator|=()", demangler.Parse("_ZoRv"));
-  ASSERT_EQ("operator->*()", demangler.Parse("_Zpmv"));
-  ASSERT_EQ("operator+()", demangler.Parse("_Zplv"));
-  ASSERT_EQ("operator+=()", demangler.Parse("_ZpLv"));
-  ASSERT_EQ("operator++()", demangler.Parse("_Zppv"));
-  ASSERT_EQ("operator+()", demangler.Parse("_Zpsv"));
-  ASSERT_EQ("operator->()", demangler.Parse("_Zptv"));
-  ASSERT_EQ("operator?()", demangler.Parse("_Zquv"));
-  ASSERT_EQ("operator%()", demangler.Parse("_Zrmv"));
-  ASSERT_EQ("operator%=()", demangler.Parse("_ZrMv"));
-  ASSERT_EQ("operator>>()", demangler.Parse("_Zrsv"));
-  ASSERT_EQ("operator>>=()", demangler.Parse("_ZrSv"));
-
-  // Spot check using an operator as part of function name.
-  ASSERT_EQ("operator&&()", demangler.Parse("_ZNaaEv"));
-  ASSERT_EQ("operator++()", demangler.Parse("_ZNppEv"));
-  ASSERT_EQ("one::operator++()", demangler.Parse("_ZN3oneppEv"));
-
-  // Spot check using an operator in an argument name.
-  ASSERT_EQ("operator+(operator|=)", demangler.Parse("_ZNpsENoRE"));
-  ASSERT_EQ("operator==()", demangler.Parse("_Zeqv"));
-  ASSERT_EQ("one(arg1::operator|=, arg2::operator==)",
-            demangler.Parse("_ZN3oneEN4arg1oREN4arg2eqE"));
-}
-
-TEST(DemangleTest, FunctionStartsWithNumber) {
-  Demangler demangler;
-
-  ASSERT_EQ("value(char, int)", demangler.Parse("_Z5valueci"));
-  ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_Z11abcdefjklmna"));
-  ASSERT_EQ("value(one, signed char)", demangler.Parse("_Z5value3onea"));
-}
-
-TEST(DemangleTest, FunctionStartsWithLPlusNumber) {
-  Demangler demangler;
-
-  ASSERT_EQ("value(char, int)", demangler.Parse("_ZL5valueci"));
-  ASSERT_EQ("abcdefjklmn(signed char)", demangler.Parse("_ZL11abcdefjklmna"));
-  ASSERT_EQ("value(one, signed char)", demangler.Parse("_ZL5value3onea"));
-}
-
-TEST(DemangleTest, StdTypes) {
-  Demangler demangler;
-
-  ASSERT_EQ("std::one", demangler.Parse("_ZNSt3oneE"));
-  ASSERT_EQ("std::one(std::two)", demangler.Parse("_ZNSt3oneESt3two"));
-  ASSERT_EQ("std::std::one(std::two)", demangler.Parse("_ZNStSt3oneESt3two"));
-  ASSERT_EQ("std()", demangler.Parse("_ZNStEv"));
-  ASSERT_EQ("one::std::std::two::~two(one::std::std::two)",
-            demangler.Parse("_ZN3oneStSt3twoD0ES0_"));
-
-  ASSERT_EQ("std::allocator", demangler.Parse("_ZNSaE"));
-  ASSERT_EQ("std::basic_string", demangler.Parse("_ZNSbE"));
-  ASSERT_EQ("_ZNScE", demangler.Parse("_ZNScE"));
-  ASSERT_EQ("std::iostream", demangler.Parse("_ZNSdE"));
-  ASSERT_EQ("_ZNSeE", demangler.Parse("_ZNSeE"));
-  ASSERT_EQ("_ZNSfE", demangler.Parse("_ZNSfE"));
-  ASSERT_EQ("_ZNSgE", demangler.Parse("_ZNSgE"));
-  ASSERT_EQ("_ZNShE", demangler.Parse("_ZNShE"));
-  ASSERT_EQ("std::istream", demangler.Parse("_ZNSiE"));
-  ASSERT_EQ("_ZNSjE", demangler.Parse("_ZNSjE"));
-  ASSERT_EQ("_ZNSkE", demangler.Parse("_ZNSkE"));
-  ASSERT_EQ("_ZNSlE", demangler.Parse("_ZNSlE"));
-  ASSERT_EQ("_ZNSmE", demangler.Parse("_ZNSmE"));
-  ASSERT_EQ("_ZNSnE", demangler.Parse("_ZNSnE"));
-  ASSERT_EQ("std::ostream", demangler.Parse("_ZNSoE"));
-  ASSERT_EQ("_ZNSpE", demangler.Parse("_ZNSpE"));
-  ASSERT_EQ("_ZNSqE", demangler.Parse("_ZNSqE"));
-  ASSERT_EQ("_ZNSrE", demangler.Parse("_ZNSrE"));
-  ASSERT_EQ("std::string", demangler.Parse("_ZNSsE"));
-  ASSERT_EQ("_ZNSuE", demangler.Parse("_ZNSuE"));
-  ASSERT_EQ("_ZNSvE", demangler.Parse("_ZNSvE"));
-  ASSERT_EQ("_ZNSwE", demangler.Parse("_ZNSwE"));
-  ASSERT_EQ("_ZNSxE", demangler.Parse("_ZNSxE"));
-  ASSERT_EQ("_ZNSyE", demangler.Parse("_ZNSyE"));
-  ASSERT_EQ("_ZNSzE", demangler.Parse("_ZNSzE"));
-}
-
-TEST(DemangleTest, SingleLetterArguments) {
-  Demangler demangler;
-
-  ASSERT_EQ("func(signed char)", demangler.Parse("_ZN4funcEa"));
-  ASSERT_EQ("func(bool)", demangler.Parse("_ZN4funcEb"));
-  ASSERT_EQ("func(char)", demangler.Parse("_ZN4funcEc"));
-  ASSERT_EQ("func(double)", demangler.Parse("_ZN4funcEd"));
-  ASSERT_EQ("func(long double)", demangler.Parse("_ZN4funcEe"));
-  ASSERT_EQ("func(float)", demangler.Parse("_ZN4funcEf"));
-  ASSERT_EQ("func(__float128)", demangler.Parse("_ZN4funcEg"));
-  ASSERT_EQ("func(unsigned char)", demangler.Parse("_ZN4funcEh"));
-  ASSERT_EQ("func(int)", demangler.Parse("_ZN4funcEi"));
-  ASSERT_EQ("func(unsigned int)", demangler.Parse("_ZN4funcEj"));
-  ASSERT_EQ("_ZN4funcEk", demangler.Parse("_ZN4funcEk"));
-  ASSERT_EQ("func(long)", demangler.Parse("_ZN4funcEl"));
-  ASSERT_EQ("func(unsigned long)", demangler.Parse("_ZN4funcEm"));
-  ASSERT_EQ("func(__int128)", demangler.Parse("_ZN4funcEn"));
-  ASSERT_EQ("func(unsigned __int128)", demangler.Parse("_ZN4funcEo"));
-  ASSERT_EQ("_ZN4funcEp", demangler.Parse("_ZN4funcEp"));
-  ASSERT_EQ("_ZN4funcEq", demangler.Parse("_ZN4funcEq"));
-  ASSERT_EQ("_ZN4funcEr", demangler.Parse("_ZN4funcEr"));
-  ASSERT_EQ("func(short)", demangler.Parse("_ZN4funcEs"));
-  ASSERT_EQ("func(unsigned short)", demangler.Parse("_ZN4funcEt"));
-  ASSERT_EQ("_ZN4funcEu", demangler.Parse("_ZN4funcEu"));
-  ASSERT_EQ("func()", demangler.Parse("_ZN4funcEv"));
-  ASSERT_EQ("func(wchar_t)", demangler.Parse("_ZN4funcEw"));
-  ASSERT_EQ("func(long long)", demangler.Parse("_ZN4funcEx"));
-  ASSERT_EQ("func(unsigned long long)", demangler.Parse("_ZN4funcEy"));
-  ASSERT_EQ("func(...)", demangler.Parse("_ZN4funcEz"));
-}
-
-TEST(DemangleTest, DArguments) {
-  Demangler demangler;
-
-  ASSERT_EQ("func(auto)", demangler.Parse("_ZN4funcEDa"));
-  ASSERT_EQ("_ZN4funcEDb", demangler.Parse("_ZN4funcEDb"));
-  ASSERT_EQ("_ZN4funcEDc", demangler.Parse("_ZN4funcEDc"));
-  ASSERT_EQ("func(decimal64)", demangler.Parse("_ZN4funcEDd"));
-  ASSERT_EQ("func(decimal128)", demangler.Parse("_ZN4funcEDe"));
-  ASSERT_EQ("func(decimal32)", demangler.Parse("_ZN4funcEDf"));
-  ASSERT_EQ("_ZN4funcEDg", demangler.Parse("_ZN4funcEDg"));
-  ASSERT_EQ("func(half)", demangler.Parse("_ZN4funcEDh"));
-  ASSERT_EQ("func(char32_t)", demangler.Parse("_ZN4funcEDi"));
-  ASSERT_EQ("_ZN4funcEDj", demangler.Parse("_ZN4funcEDj"));
-  ASSERT_EQ("_ZN4funcEDk", demangler.Parse("_ZN4funcEDk"));
-  ASSERT_EQ("_ZN4funcEDl", demangler.Parse("_ZN4funcEDl"));
-  ASSERT_EQ("_ZN4funcEDm", demangler.Parse("_ZN4funcEDm"));
-  ASSERT_EQ("func(decltype(nullptr))", demangler.Parse("_ZN4funcEDn"));
-  ASSERT_EQ("_ZN4funcEDo", demangler.Parse("_ZN4funcEDo"));
-  ASSERT_EQ("_ZN4funcEDp", demangler.Parse("_ZN4funcEDp"));
-  ASSERT_EQ("_ZN4funcEDq", demangler.Parse("_ZN4funcEDq"));
-  ASSERT_EQ("_ZN4funcEDr", demangler.Parse("_ZN4funcEDr"));
-  ASSERT_EQ("func(char16_t)", demangler.Parse("_ZN4funcEDs"));
-  ASSERT_EQ("_ZN4funcEDt", demangler.Parse("_ZN4funcEDt"));
-  ASSERT_EQ("_ZN4funcEDu", demangler.Parse("_ZN4funcEDu"));
-  ASSERT_EQ("_ZN4funcEDv", demangler.Parse("_ZN4funcEDv"));
-  ASSERT_EQ("_ZN4funcEDw", demangler.Parse("_ZN4funcEDw"));
-  ASSERT_EQ("_ZN4funcEDx", demangler.Parse("_ZN4funcEDx"));
-  ASSERT_EQ("_ZN4funcEDy", demangler.Parse("_ZN4funcEDy"));
-  ASSERT_EQ("_ZN4funcEDz", demangler.Parse("_ZN4funcEDz"));
-}
-
-TEST(DemangleTest, FunctionArguments) {
-  Demangler demangler;
-
-  ASSERT_EQ("func(char ())", demangler.Parse("_ZN4funcEFcvE"));
-  ASSERT_EQ("func(char (*)())", demangler.Parse("_ZN4funcEPFcvE"));
-  ASSERT_EQ("func(char (&)())", demangler.Parse("_ZN4funcERFcvE"));
-  ASSERT_EQ("func(char (&)())", demangler.Parse("_ZN4funcERFcvE"));
-  ASSERT_EQ("func(char (*&)())", demangler.Parse("_ZN4funcERPFcvE"));
-  ASSERT_EQ("func(char (*)(int) const)", demangler.Parse("_ZN4funcEPKFciE"));
-  ASSERT_EQ("func(char (&)() const)", demangler.Parse("_ZN4funcERKFcvE"));
-  ASSERT_EQ("func(char (&)() volatile)", demangler.Parse("_ZN4funcERVFcvE"));
-  ASSERT_EQ("func(char (&)() volatile const)", demangler.Parse("_ZN4funcERKVFcvE"));
-  ASSERT_EQ("func(char (&)() const volatile)", demangler.Parse("_ZN4funcERVKFcvE"));
-  ASSERT_EQ("func(char (&)(int, signed char) const)", demangler.Parse("_ZN4funcERKFciaE"));
-  ASSERT_EQ("fake(char (&* volatile const)(void, void, signed char), signed char)",
-            demangler.Parse("_ZN4fakeEKVPRFcvvaEa"));
-}
-
-TEST(DemangleTest, TemplateFunction) {
-  Demangler demangler;
-
-  ASSERT_EQ("one<char>", demangler.Parse("_ZN3oneIcEE"));
-  ASSERT_EQ("one<void>", demangler.Parse("_ZN3oneIvEE"));
-  ASSERT_EQ("one<void*>", demangler.Parse("_ZN3oneIPvEE"));
-  ASSERT_EQ("one<void const>", demangler.Parse("_ZN3oneIKvEE"));
-  ASSERT_EQ("one<char, int, bool>", demangler.Parse("_ZN3oneIcibEE"));
-  ASSERT_EQ("one::two<three>", demangler.Parse("_ZN3one3twoIN5threeEEE"));
-  ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_ZN3oneIciN3two5threeEEE"));
-  // Template within templates.
-  ASSERT_EQ("one::two<three<char, int>>", demangler.Parse("_ZN3one3twoIN5threeIciEEEE"));
-  ASSERT_EQ("one::two<three<char, four<int>>>", demangler.Parse("_ZN3one3twoIN5threeIcN4fourIiEEEEEE"));
-
-  ASSERT_EQ("one<char>", demangler.Parse("_Z3oneIcE"));
-  ASSERT_EQ("one<void>", demangler.Parse("_Z3oneIvE"));
-  ASSERT_EQ("one<void*>", demangler.Parse("_Z3oneIPvE"));
-  ASSERT_EQ("one<void const>", demangler.Parse("_Z3oneIKvE"));
-  ASSERT_EQ("one<char, int, bool>", demangler.Parse("_Z3oneIcibE"));
-  ASSERT_EQ("one(two<three>)", demangler.Parse("_Z3one3twoIN5threeEE"));
-  ASSERT_EQ("one<char, int, two::three>", demangler.Parse("_Z3oneIciN3two5threeEE"));
-  // Template within templates.
-  ASSERT_EQ("one(two<three<char, int>>)", demangler.Parse("_Z3one3twoIN5threeIciEEE"));
-  ASSERT_EQ("one(two<three<char, four<int>>>)",
-            demangler.Parse("_Z3one3twoIN5threeIcN4fourIiEEEEE"));
-}
-
-TEST(DemangleTest, TemplateFunctionWithReturnType) {
-  Demangler demangler;
-
-  ASSERT_EQ("char one<int>(char)", demangler.Parse("_Z3oneIiEcc"));
-  ASSERT_EQ("void one<int>()", demangler.Parse("_Z3oneIiEvv"));
-  ASSERT_EQ("char one<int>()", demangler.Parse("_Z3oneIiEcv"));
-  ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_Z3oneIiEcvv"));
-  ASSERT_EQ("char one<int>()", demangler.Parse("_ZN3oneIiEEcv"));
-  ASSERT_EQ("char one<int>(void, void)", demangler.Parse("_ZN3oneIiEEcvv"));
-}
-
-TEST(DemangleTest, TemplateArguments) {
-  Demangler demangler;
-
-  ASSERT_EQ("one(two<char>)", demangler.Parse("_ZN3oneE3twoIcE"));
-  ASSERT_EQ("one(two<char, void>)", demangler.Parse("_ZN3oneE3twoIcvE"));
-  ASSERT_EQ("one(two<char, void, three<four, int>>)",
-            demangler.Parse("_ZN3oneE3twoIcv5threeI4fouriEE"));
-}
-
-TEST(DemangleTest, SubstitutionUnderscore) {
-  Demangler demangler;
-
-  ASSERT_EQ("a::a", demangler.Parse("_ZN1aS_E"));
-  ASSERT_EQ("one::one", demangler.Parse("_ZN3oneS_E"));
-  ASSERT_EQ("one::two::one", demangler.Parse("_ZN3one3twoS_E"));
-  ASSERT_EQ("one::two::three::one", demangler.Parse("_ZN3one3two5threeS_E"));
-  ASSERT_EQ("one::two(one)", demangler.Parse("_ZN3one3twoES_"));
-  ASSERT_EQ("one::two(three::one)", demangler.Parse("_ZN3one3twoEN5threeS_E"));
-
-  // Special case that St is part of the saved value used in the substitution.
-  ASSERT_EQ("std::one::std::one", demangler.Parse("_ZNSt3oneS_E"));
-
-  // Multiple substitutions in the string.
-  ASSERT_EQ("one::one(one, one)", demangler.Parse("_ZN3oneS_ES_S_"));
-  ASSERT_EQ("std::one::two::std::one(std::one)", demangler.Parse("_ZNSt3one3twoS_ES_"));
-}
-
-TEST(DemangleTest, SubstitutionByNumber) {
-  Demangler demangler;
-
-  // Basic substitution.
-  ASSERT_EQ("a::b::c(a::b)", demangler.Parse("_ZN1a1b1cES0_"));
-  ASSERT_EQ("_ZN1a1b1cES1_", demangler.Parse("_ZN1a1b1cES1_"));
-  ASSERT_EQ("a::b::c::d(a::b::c)", demangler.Parse("_ZN1a1b1c1dES1_"));
-  ASSERT_EQ("a::b::c::d::e::f::g::h::i::j::k::l::m::n::o::p::q(a::b::c::d::e::f::g::h::i::j::k::l)",
-            demangler.Parse("_ZN1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1qESA_"));
-  ASSERT_EQ("a::b::c::d::e::f::g::h::i::j::k::l::m::n::o::p::q(a::b::c::d::e::f::g::h::i::j::k::l::m)",
-            demangler.Parse("_ZN1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1qESB_"));
-
-  // Verify argument modifiers are included in substitution list.
-  ASSERT_EQ("one::two(char&* volatile const, char&)", demangler.Parse("_ZN3one3twoEKVPRcS0_"));
-  ASSERT_EQ("one::two(char&* volatile const, char&*)", demangler.Parse("_ZN3one3twoEKVPRcS1_"));
-  ASSERT_EQ("one::two(char&* volatile const, char&* volatile const)",
-            demangler.Parse("_ZN3one3twoEKVPRcS2_"));
-  ASSERT_EQ("one::two(int&* volatile* const, int&)", demangler.Parse("_ZN3one3twoEKPVPRiS0_"));
-  ASSERT_EQ("one::two(int&* volatile const, int&*)", demangler.Parse("_ZN3one3twoEKVPRiS1_"));
-  ASSERT_EQ("one::two(int&* volatile const, int&* volatile const)",
-            demangler.Parse("_ZN3one3twoEKVPRiS2_"));
-
-  // Verify Constructor/Destructor does properly save from function name.
-  ASSERT_EQ("_ZN1a1bES0_", demangler.Parse("_ZN1a1bES0_"));
-  ASSERT_EQ("a::b::b(a::b)", demangler.Parse("_ZN1a1bC1ES0_"));
-  ASSERT_EQ("a::b::~b(a::b)", demangler.Parse("_ZN1a1bD0ES0_"));
-
-  // Make sure substitution values are not saved.
-  ASSERT_EQ("a::b::b(a::b, char*, char*)", demangler.Parse("_ZN1a1bC1ES0_PcS1_"));
-}
-
-TEST(DemangleTest, ComplexSubstitution) {
-  Demangler demangler;
-
-  ASSERT_EQ("one::two<one::three>::two()", demangler.Parse("_ZN3one3twoINS_5threeEEC1Ev"));
-  ASSERT_EQ("one::two::two(one::two const&, bool, one::three*)",
-            demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE"));
-  ASSERT_EQ("one::two::three::four<one::five>::~four(one::two*)",
-            demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS0_"));
-  ASSERT_EQ("one::two::three::four<one::five>::~four(one::two::three*)",
-            demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS1_"));
-  ASSERT_EQ("one::two::three::four<one::five>::~four(one::two::three::four*)",
-            demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS2_"));
-  ASSERT_EQ("one::two::three::four<one::five>::~four(one::five*)",
-            demangler.Parse("_ZN3one3two5three4fourINS_4fiveEED2EPS3_"));
-}
-
-TEST(DemangleTest, TemplateSubstitution) {
-  Demangler demangler;
-
-  ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_ZN3oneIidEEvT_"));
-  ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_ZN3oneIidEEvT0_"));
-  ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_ZN3oneIidcvEEvT1_"));
-
-  ASSERT_EQ("void one<int, double>(int)", demangler.Parse("_Z3oneIidEvT_"));
-  ASSERT_EQ("void one<int, double>(double)", demangler.Parse("_Z3oneIidEvT0_"));
-  ASSERT_EQ("void one<int, double, char, void>(char)", demangler.Parse("_Z3oneIidcvEvT1_"));
-
-  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
-            demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT10_"));
-  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
-            demangler.Parse("_ZN3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEEvT11_"));
-
-  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(l)",
-            demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT10_"));
-  ASSERT_EQ("void one<a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r>(m)",
-            demangler.Parse("_Z3oneI1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1rEvT11_"));
-}
-
-TEST(DemangleTest, StringTooLong) {
-  Demangler demangler;
-
-  ASSERT_EQ("_ZN3one3twoC2ERKS0_bPNS_5threeE",
-            demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 10));
-  ASSERT_EQ("_ZN3one3twoC2ERKS0_bPNS_5threeE",
-            demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 30));
-  ASSERT_EQ("one::two::two(one::two const&, bool, one::three*)",
-            demangler.Parse("_ZN3one3twoC2ERKS0_bPNS_5threeE", 31));
-
-  // Check the length check only occurs after the two letter value
-  // has been processed.
-  ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 15));
-  ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 14));
-  ASSERT_EQ("one::two(auto)", demangler.Parse("_ZN3one3twoEDa", 13));
-  ASSERT_EQ("_ZN3one3twoEDa", demangler.Parse("_ZN3one3twoEDa", 12));
-}
-
-TEST(DemangleTest, BooleanLiterals) {
-  Demangler demangler;
-
-  ASSERT_EQ("one<true>", demangler.Parse("_ZN3oneILb1EEE"));
-  ASSERT_EQ("one<false>", demangler.Parse("_ZN3oneILb0EEE"));
-  ASSERT_EQ("one<false, true>", demangler.Parse("_ZN3oneILb0ELb1EEE"));
-
-  ASSERT_EQ("one<true>", demangler.Parse("_Z3oneILb1EE"));
-  ASSERT_EQ("one<false>", demangler.Parse("_Z3oneILb0EE"));
-  ASSERT_EQ("one<false, true>", demangler.Parse("_Z3oneILb0ELb1EE"));
-
-  ASSERT_EQ("one(two<three<four>, false, true>)",
-            demangler.Parse("_ZN3oneE3twoI5threeI4fourELb0ELb1EE"));
-}
-
-TEST(DemangleTest, non_virtual_thunk) {
-  Demangler demangler;
-
-  ASSERT_EQ("non-virtual thunk to one", demangler.Parse("_ZThn0_N3oneE"));
-  ASSERT_EQ("non-virtual thunk to two", demangler.Parse("_ZThn0_3two"));
-  ASSERT_EQ("non-virtual thunk to three", demangler.Parse("_ZTh0_5three"));
-  ASSERT_EQ("non-virtual thunk to four", demangler.Parse("_ZTh_4four"));
-  ASSERT_EQ("non-virtual thunk to five", demangler.Parse("_ZTh0123456789_4five"));
-  ASSERT_EQ("non-virtual thunk to six", demangler.Parse("_ZThn0123456789_3six"));
-
-  ASSERT_EQ("_ZThn0N3oneE", demangler.Parse("_ZThn0N3oneE"));
-  ASSERT_EQ("_ZThn03two", demangler.Parse("_ZThn03two"));
-  ASSERT_EQ("_ZTh05three", demangler.Parse("_ZTh05three"));
-  ASSERT_EQ("_ZTh4four", demangler.Parse("_ZTh4four"));
-  ASSERT_EQ("_ZTh01234567894five", demangler.Parse("_ZTh01234567894five"));
-  ASSERT_EQ("_ZThn01234567893six", demangler.Parse("_ZThn01234567893six"));
-  ASSERT_EQ("_ZT_N3oneE", demangler.Parse("_ZT_N3oneE"));
-  ASSERT_EQ("_ZT0_N3oneE", demangler.Parse("_ZT0_N3oneE"));
-  ASSERT_EQ("_ZTH_N3oneE", demangler.Parse("_ZTH_N3oneE"));
-}
-
-TEST(DemangleTest, r_value_reference) {
-  Demangler demangler;
-  ASSERT_EQ(
-      "android::SurfaceComposerClient::Transaction::merge(android::SurfaceComposerClient::"
-      "Transaction&&)",
-      demangler.Parse("_ZN7android21SurfaceComposerClient11Transaction5mergeEOS1_"));
-}
-
-TEST(DemangleTest, initial_St) {
-  Demangler demangler;
-  EXPECT_EQ("std::state", demangler.Parse("_ZSt5state"));
-  EXPECT_EQ("std::_In::ward", demangler.Parse("_ZNSt3_In4wardE"));
-  EXPECT_EQ("std::__terminate(void (*)())", demangler.Parse("_ZSt11__terminatePFvvE"));
-}
-
-TEST(DemangleTest, cfi) {
-  Demangler demangler;
-  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*)",
-            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB"));
-  EXPECT_EQ("nfa_sys_ptim_timer_update(tPTIM_CB*) [clone .cfi]",
-            demangler.Parse("_Z25nfa_sys_ptim_timer_updateP8tPTIM_CB.cfi"));
-}
-
-TEST(DemangleTest, demangle) {
-  std::string str;
-
-  str = demangle("_ZN1a1b1cES0_");
-  ASSERT_EQ("a::b::c(a::b)", str);
-
-  str = demangle("_");
-  ASSERT_EQ("_", str);
-
-  str = demangle("_Z");
-  ASSERT_EQ("_Z", str);
-
-  str = demangle("_Za");
-  ASSERT_EQ("_Za", str);
-
-  str = demangle("_Zaa");
-  ASSERT_EQ("operator&&", str);
-
-  str = demangle("Xa");
-  ASSERT_EQ("Xa", str);
-}
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
deleted file mode 100644
index 7bae356..0000000
--- a/demangle/Demangler.cpp
+++ /dev/null
@@ -1,925 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-#include <string.h>
-
-#include <cctype>
-#include <stack>
-#include <string>
-#include <vector>
-
-#include "Demangler.h"
-
-constexpr const char* Demangler::kTypes[];
-constexpr const char* Demangler::kDTypes[];
-constexpr const char* Demangler::kSTypes[];
-
-void Demangler::Save(const std::string& str, bool is_name) {
-  saves_.push_back(str);
-  last_save_name_ = is_name;
-}
-
-std::string Demangler::GetArgumentsString() {
-  size_t num_args = cur_state_.args.size();
-  std::string arg_str;
-  if (num_args > 0) {
-    arg_str = cur_state_.args[0];
-    for (size_t i = 1; i < num_args; i++) {
-      arg_str += ", " + cur_state_.args[i];
-    }
-  }
-  return arg_str;
-}
-
-const char* Demangler::AppendOperatorString(const char* name) {
-  const char* oper = nullptr;
-  switch (*name) {
-  case 'a':
-    name++;
-    switch (*name) {
-    case 'a':
-      oper = "operator&&";
-      break;
-    case 'd':
-    case 'n':
-      oper = "operator&";
-      break;
-    case 'N':
-      oper = "operator&=";
-      break;
-    case 'S':
-      oper = "operator=";
-      break;
-    }
-    break;
-  case 'c':
-    name++;
-    switch (*name) {
-    case 'l':
-      oper = "operator()";
-      break;
-    case 'm':
-      oper = "operator,";
-      break;
-    case 'o':
-      oper = "operator~";
-      break;
-    }
-    break;
-  case 'd':
-    name++;
-    switch (*name) {
-    case 'a':
-      oper = "operator delete[]";
-      break;
-    case 'e':
-      oper = "operator*";
-      break;
-    case 'l':
-      oper = "operator delete";
-      break;
-    case 'v':
-      oper = "operator/";
-      break;
-    case 'V':
-      oper = "operator/=";
-      break;
-    }
-    break;
-  case 'e':
-    name++;
-    switch (*name) {
-    case 'o':
-      oper = "operator^";
-      break;
-    case 'O':
-      oper = "operator^=";
-      break;
-    case 'q':
-      oper = "operator==";
-      break;
-    }
-    break;
-  case 'g':
-    name++;
-    switch (*name) {
-    case 'e':
-      oper = "operator>=";
-      break;
-    case 't':
-      oper = "operator>";
-      break;
-    }
-    break;
-  case 'i':
-    name++;
-    switch (*name) {
-    case 'x':
-      oper = "operator[]";
-      break;
-    }
-    break;
-  case 'l':
-    name++;
-    switch (*name) {
-    case 'e':
-      oper = "operator<=";
-      break;
-    case 's':
-      oper = "operator<<";
-      break;
-    case 'S':
-      oper = "operator<<=";
-      break;
-    case 't':
-      oper = "operator<";
-      break;
-    }
-    break;
-  case 'm':
-    name++;
-    switch (*name) {
-    case 'i':
-      oper = "operator-";
-      break;
-    case 'I':
-      oper = "operator-=";
-      break;
-    case 'l':
-      oper = "operator*";
-      break;
-    case 'L':
-      oper = "operator*=";
-      break;
-    case 'm':
-      oper = "operator--";
-      break;
-    }
-    break;
-  case 'n':
-    name++;
-    switch (*name) {
-    case 'a':
-      oper = "operator new[]";
-      break;
-    case 'e':
-      oper = "operator!=";
-      break;
-    case 'g':
-      oper = "operator-";
-      break;
-    case 't':
-      oper = "operator!";
-      break;
-    case 'w':
-      oper = "operator new";
-      break;
-    }
-    break;
-  case 'o':
-    name++;
-    switch (*name) {
-    case 'o':
-      oper = "operator||";
-      break;
-    case 'r':
-      oper = "operator|";
-      break;
-    case 'R':
-      oper = "operator|=";
-      break;
-    }
-    break;
-  case 'p':
-    name++;
-    switch (*name) {
-    case 'm':
-      oper = "operator->*";
-      break;
-    case 'l':
-      oper = "operator+";
-      break;
-    case 'L':
-      oper = "operator+=";
-      break;
-    case 'p':
-      oper = "operator++";
-      break;
-    case 's':
-      oper = "operator+";
-      break;
-    case 't':
-      oper = "operator->";
-      break;
-    }
-    break;
-  case 'q':
-    name++;
-    switch (*name) {
-    case 'u':
-      oper = "operator?";
-      break;
-    }
-    break;
-  case 'r':
-    name++;
-    switch (*name) {
-    case 'm':
-      oper = "operator%";
-      break;
-    case 'M':
-      oper = "operator%=";
-      break;
-    case 's':
-      oper = "operator>>";
-      break;
-    case 'S':
-      oper = "operator>>=";
-      break;
-    }
-    break;
-  }
-  if (oper == nullptr) {
-    return nullptr;
-  }
-  AppendCurrent(oper);
-  cur_state_.last_save = oper;
-  return name + 1;
-}
-
-const char* Demangler::GetStringFromLength(const char* name, std::string* str) {
-  assert(std::isdigit(*name));
-
-  size_t length = *name - '0';
-  name++;
-  while (*name != '\0' && std::isdigit(*name)) {
-    length = length * 10 + *name - '0';
-    name++;
-  }
-
-  std::string read_str;
-  while (*name != '\0' && length != 0) {
-    read_str += *name;
-    name++;
-    length--;
-  }
-  if (length != 0) {
-    return nullptr;
-  }
-  // Special replacement of _GLOBAL__N_1 to (anonymous namespace).
-  if (read_str == "_GLOBAL__N_1") {
-    *str += "(anonymous namespace)";
-  } else {
-    *str += read_str;
-  }
-  return name;
-}
-
-void Demangler::AppendCurrent(const std::string& str) {
-  if (!cur_state_.str.empty()) {
-    cur_state_.str += "::";
-  }
-  cur_state_.str += str;
-}
-
-void Demangler::AppendCurrent(const char* str) {
-  if (!cur_state_.str.empty()) {
-    cur_state_.str += "::";
-  }
-  cur_state_.str += str;
-}
-
-const char* Demangler::ParseS(const char* name) {
-  if (std::islower(*name)) {
-    const char* type = kSTypes[*name - 'a'];
-    if (type == nullptr) {
-      return nullptr;
-    }
-    AppendCurrent(type);
-    return name + 1;
-  }
-
-  if (saves_.empty()) {
-    return nullptr;
-  }
-
-  if (*name == '_') {
-    last_save_name_ = false;
-    AppendCurrent(saves_[0]);
-    return name + 1;
-  }
-
-  bool isdigit = std::isdigit(*name);
-  if (!isdigit && !std::isupper(*name)) {
-    return nullptr;
-  }
-
-  size_t index;
-  if (isdigit) {
-    index = *name - '0' + 1;
-  } else {
-    index = *name - 'A' + 11;
-  }
-  name++;
-  if (*name != '_') {
-    return nullptr;
-  }
-
-  if (index >= saves_.size()) {
-    return nullptr;
-  }
-
-  last_save_name_ = false;
-  AppendCurrent(saves_[index]);
-  return name + 1;
-}
-
-const char* Demangler::ParseT(const char* name) {
-  if (template_saves_.empty()) {
-    return nullptr;
-  }
-
-  if (*name == '_') {
-    last_save_name_ = false;
-    AppendCurrent(template_saves_[0]);
-    return name + 1;
-  }
-
-  // Need to get the total number.
-  char* end;
-  unsigned long int index = strtoul(name, &end, 10) + 1;
-  if (name == end || *end != '_') {
-    return nullptr;
-  }
-
-  if (index >= template_saves_.size()) {
-    return nullptr;
-  }
-
-  last_save_name_ = false;
-  AppendCurrent(template_saves_[index]);
-  return end + 1;
-}
-
-const char* Demangler::ParseFunctionName(const char* name) {
-  if (*name == 'E') {
-    if (parse_funcs_.empty()) {
-      return nullptr;
-    }
-    parse_func_ = parse_funcs_.back();
-    parse_funcs_.pop_back();
-
-    // Remove the last saved part so that the full function name is not saved.
-    // But only if the last save was not something like a substitution.
-    if (!saves_.empty() && last_save_name_) {
-      saves_.pop_back();
-    }
-
-    function_name_ += cur_state_.str;
-    while (!cur_state_.suffixes.empty()) {
-      function_suffix_ += cur_state_.suffixes.back();
-      cur_state_.suffixes.pop_back();
-    }
-    cur_state_.Clear();
-
-    return name + 1;
-  }
-
-  if (*name == 'I') {
-    state_stack_.push(cur_state_);
-    cur_state_.Clear();
-
-    parse_funcs_.push_back(parse_func_);
-    parse_func_ = &Demangler::ParseFunctionNameTemplate;
-    return name + 1;
-  }
-
-  return ParseComplexString(name);
-}
-
-const char* Demangler::ParseFunctionNameTemplate(const char* name) {
-  if (*name == 'E' && name[1] == 'E') {
-    // Only consider this a template with saves if it is right before
-    // the end of the name.
-    template_found_ = true;
-    template_saves_ = cur_state_.args;
-  }
-  return ParseTemplateArgumentsComplex(name);
-}
-
-const char* Demangler::ParseComplexArgument(const char* name) {
-  if (*name == 'E') {
-    if (parse_funcs_.empty()) {
-      return nullptr;
-    }
-    parse_func_ = parse_funcs_.back();
-    parse_funcs_.pop_back();
-
-    AppendArgument(cur_state_.str);
-    cur_state_.str.clear();
-
-    return name + 1;
-  }
-
-  return ParseComplexString(name);
-}
-
-void Demangler::FinalizeTemplate() {
-  std::string arg_str(GetArgumentsString());
-  cur_state_ = state_stack_.top();
-  state_stack_.pop();
-  cur_state_.str += '<' + arg_str + '>';
-}
-
-const char* Demangler::ParseComplexString(const char* name) {
-  if (*name == 'S') {
-    name++;
-    if (*name == 't') {
-      AppendCurrent("std");
-      return name + 1;
-    }
-    return ParseS(name);
-  }
-  if (*name == 'L') {
-    name++;
-    if (!std::isdigit(*name)) {
-      return nullptr;
-    }
-  }
-  if (std::isdigit(*name)) {
-    std::string str;
-    name = GetStringFromLength(name, &str);
-    if (name == nullptr) {
-      return name;
-    }
-    AppendCurrent(str);
-    Save(cur_state_.str, true);
-    cur_state_.last_save = std::move(str);
-    return name;
-  }
-  if (*name == 'D') {
-    name++;
-    if (saves_.empty() || (*name != '0' && *name != '1' && *name != '2'
-        && *name != '5')) {
-      return nullptr;
-    }
-    last_save_name_ = false;
-    AppendCurrent("~" + cur_state_.last_save);
-    return name + 1;
-  }
-  if (*name == 'C') {
-    name++;
-    if (saves_.empty() || (*name != '1' && *name != '2' && *name != '3'
-        && *name != '5')) {
-      return nullptr;
-    }
-    last_save_name_ = false;
-    AppendCurrent(cur_state_.last_save);
-    return name + 1;
-  }
-  if (*name == 'K') {
-    cur_state_.suffixes.push_back(" const");
-    return name + 1;
-  }
-  if (*name == 'V') {
-    cur_state_.suffixes.push_back(" volatile");
-    return name + 1;
-  }
-  if (*name == 'I') {
-    // Save the current argument state.
-    state_stack_.push(cur_state_);
-    cur_state_.Clear();
-
-    parse_funcs_.push_back(parse_func_);
-    parse_func_ = &Demangler::ParseTemplateArgumentsComplex;
-    return name + 1;
-  }
-  name = AppendOperatorString(name);
-  if (name != nullptr) {
-    Save(cur_state_.str, true);
-  }
-  return name;
-}
-
-void Demangler::AppendArgument(const std::string& str) {
-  std::string arg(str);
-  while (!cur_state_.suffixes.empty()) {
-    arg += cur_state_.suffixes.back();
-    cur_state_.suffixes.pop_back();
-    Save(arg, false);
-  }
-  cur_state_.args.push_back(arg);
-}
-
-const char* Demangler::ParseFunctionArgument(const char* name) {
-  if (*name == 'E') {
-    // The first argument is the function modifier.
-    // The second argument is the function type.
-    // The third argument is the return type of the function.
-    // The rest of the arguments are the function arguments.
-    size_t num_args = cur_state_.args.size();
-    if (num_args < 4) {
-      return nullptr;
-    }
-    std::string function_modifier = cur_state_.args[0];
-    std::string function_type = cur_state_.args[1];
-
-    std::string str = cur_state_.args[2] + ' ';
-    if (!cur_state_.args[1].empty()) {
-      str += '(' + cur_state_.args[1] + ')';
-    }
-
-    if (num_args == 4 && cur_state_.args[3] == "void") {
-      str += "()";
-    } else {
-      str += '(' + cur_state_.args[3];
-      for (size_t i = 4; i < num_args; i++) {
-        str += ", " + cur_state_.args[i];
-      }
-      str += ')';
-    }
-    str += cur_state_.args[0];
-
-    cur_state_ = state_stack_.top();
-    state_stack_.pop();
-    cur_state_.args.emplace_back(std::move(str));
-
-    parse_func_ = parse_funcs_.back();
-    parse_funcs_.pop_back();
-    return name + 1;
-  }
-  return ParseArguments(name);
-}
-
-const char* Demangler::ParseArguments(const char* name) {
-  switch (*name) {
-  case 'P':
-    cur_state_.suffixes.push_back("*");
-    return name + 1;
-
-  case 'R':
-    // This should always be okay because the string is guaranteed to have
-    // at least two characters before this. A mangled string always starts
-    // with _Z.
-    if (name[-1] != 'R') {
-      // Multiple 'R's in a row only add a single &.
-      cur_state_.suffixes.push_back("&");
-    }
-    return name + 1;
-
-  case 'O':
-    cur_state_.suffixes.push_back("&&");
-    return name + 1;
-
-  case 'K':
-  case 'V': {
-    const char* suffix;
-    if (*name == 'K') {
-      suffix = " const";
-    } else {
-      suffix = " volatile";
-    }
-    if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) {
-      // Special case, const/volatile apply as a single entity.
-      size_t index = cur_state_.suffixes.size();
-      cur_state_.suffixes[index-1].insert(0, suffix);
-    } else {
-      cur_state_.suffixes.push_back(suffix);
-    }
-    return name + 1;
-  }
-
-  case 'F': {
-    std::string function_modifier;
-    std::string function_type;
-    if (!cur_state_.suffixes.empty()) {
-      // If the first element starts with a ' ', then this modifies the
-      // function itself.
-      if (cur_state_.suffixes.back()[0] == ' ') {
-        function_modifier = cur_state_.suffixes.back();
-        cur_state_.suffixes.pop_back();
-      }
-      while (!cur_state_.suffixes.empty()) {
-        function_type += cur_state_.suffixes.back();
-        cur_state_.suffixes.pop_back();
-      }
-    }
-
-    state_stack_.push(cur_state_);
-
-    cur_state_.Clear();
-
-    // The function parameter has this format:
-    //   First argument is the function modifier.
-    //   Second argument is the function type.
-    //   Third argument will be the return function type but has not
-    //     been parsed yet.
-    //   Any other parameters are the arguments to the function. There
-    //     must be at least one or this isn't valid.
-    cur_state_.args.push_back(function_modifier);
-    cur_state_.args.push_back(function_type);
-
-    parse_funcs_.push_back(parse_func_);
-    parse_func_ = &Demangler::ParseFunctionArgument;
-    return name + 1;
-  }
-
-  case 'N':
-    parse_funcs_.push_back(parse_func_);
-    parse_func_ = &Demangler::ParseComplexArgument;
-    return name + 1;
-
-  case 'S':
-    name++;
-    if (*name == 't') {
-      cur_state_.str = "std::";
-      return name + 1;
-    }
-    name = ParseS(name);
-    if (name == nullptr) {
-      return nullptr;
-    }
-    AppendArgument(cur_state_.str);
-    cur_state_.str.clear();
-    return name;
-
-  case 'D':
-    name++;
-    if (*name >= 'a' && *name <= 'z') {
-      const char* arg = Demangler::kDTypes[*name - 'a'];
-      if (arg == nullptr) {
-        return nullptr;
-      }
-      AppendArgument(arg);
-      return name + 1;
-    }
-    return nullptr;
-
-  case 'I':
-    // Save the current argument state.
-    state_stack_.push(cur_state_);
-    cur_state_.Clear();
-
-    parse_funcs_.push_back(parse_func_);
-    parse_func_ = &Demangler::ParseTemplateArguments;
-    return name + 1;
-
-  case 'v':
-    AppendArgument("void");
-    return name + 1;
-
-  default:
-    if (*name >= 'a' && *name <= 'z') {
-      const char* arg = Demangler::kTypes[*name - 'a'];
-      if (arg == nullptr) {
-        return nullptr;
-      }
-      AppendArgument(arg);
-      return name + 1;
-    } else if (std::isdigit(*name)) {
-      std::string arg = cur_state_.str;
-      name = GetStringFromLength(name, &arg);
-      if (name == nullptr) {
-        return nullptr;
-      }
-      Save(arg, true);
-      if (*name == 'I') {
-        // There is one case where this argument is not complete, and that's
-        // where this is a template argument.
-        cur_state_.str = arg;
-      } else {
-        AppendArgument(arg);
-        cur_state_.str.clear();
-      }
-      return name;
-    } else if (strcmp(name, ".cfi") == 0) {
-      function_suffix_ += " [clone .cfi]";
-      return name + 4;
-    }
-  }
-  return nullptr;
-}
-
-const char* Demangler::ParseTemplateLiteral(const char* name) {
-  if (*name == 'E') {
-    parse_func_ = parse_funcs_.back();
-    parse_funcs_.pop_back();
-    return name + 1;
-  }
-  // Only understand boolean values with 0 or 1.
-  if (*name == 'b') {
-    name++;
-    if (*name == '0') {
-      AppendArgument("false");
-      cur_state_.str.clear();
-    } else if (*name == '1') {
-      AppendArgument("true");
-      cur_state_.str.clear();
-    } else {
-      return nullptr;
-    }
-    return name + 1;
-  }
-  return nullptr;
-}
-
-const char* Demangler::ParseTemplateArgumentsComplex(const char* name) {
-  if (*name == 'E') {
-    if (parse_funcs_.empty()) {
-      return nullptr;
-    }
-    parse_func_ = parse_funcs_.back();
-    parse_funcs_.pop_back();
-
-    FinalizeTemplate();
-    Save(cur_state_.str, false);
-    return name + 1;
-  } else if (*name == 'L') {
-    // Literal value for a template.
-    parse_funcs_.push_back(parse_func_);
-    parse_func_ = &Demangler::ParseTemplateLiteral;
-    return name + 1;
-  }
-
-  return ParseArguments(name);
-}
-
-const char* Demangler::ParseTemplateArguments(const char* name) {
-  if (*name == 'E') {
-    if (parse_funcs_.empty()) {
-      return nullptr;
-    }
-    parse_func_ = parse_funcs_.back();
-    parse_funcs_.pop_back();
-    FinalizeTemplate();
-    AppendArgument(cur_state_.str);
-    cur_state_.str.clear();
-    return name + 1;
-  } else if (*name == 'L') {
-    // Literal value for a template.
-    parse_funcs_.push_back(parse_func_);
-    parse_func_ = &Demangler::ParseTemplateLiteral;
-    return name + 1;
-  }
-
-  return ParseArguments(name);
-}
-
-const char* Demangler::ParseFunctionTemplateArguments(const char* name) {
-  if (*name == 'E') {
-    parse_func_ = parse_funcs_.back();
-    parse_funcs_.pop_back();
-
-    function_name_ += '<' + GetArgumentsString() + '>';
-    template_found_ = true;
-    template_saves_ = cur_state_.args;
-    cur_state_.Clear();
-    return name + 1;
-  }
-  return ParseTemplateArgumentsComplex(name);
-}
-
-const char* Demangler::FindFunctionName(const char* name) {
-  if (*name == 'T') {
-    // non-virtual thunk, verify that it matches one of these patterns:
-    //   Thn[0-9]+_
-    //   Th[0-9]+_
-    //   Thn_
-    //   Th_
-    name++;
-    if (*name != 'h') {
-      return nullptr;
-    }
-    name++;
-    if (*name == 'n') {
-      name++;
-    }
-    while (std::isdigit(*name)) {
-      name++;
-    }
-    if (*name != '_') {
-      return nullptr;
-    }
-    function_name_ = "non-virtual thunk to ";
-    return name + 1;
-  }
-
-  if (*name == 'N') {
-    parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
-    parse_func_ = &Demangler::ParseFunctionName;
-    return name + 1;
-  }
-
-  if (*name == 'S') {
-    name++;
-    if (*name == 't') {
-      function_name_ = "std::";
-      name++;
-    } else {
-      return nullptr;
-    }
-  }
-
-  if (std::isdigit(*name)) {
-    name = GetStringFromLength(name, &function_name_);
-  } else if (*name == 'L' && std::isdigit(name[1])) {
-    name = GetStringFromLength(name + 1, &function_name_);
-  } else {
-    name = AppendOperatorString(name);
-    function_name_ = cur_state_.str;
-  }
-  cur_state_.Clear();
-
-  // Check for a template argument, which will still be part of the function
-  // name.
-  if (name != nullptr && *name == 'I') {
-    parse_funcs_.push_back(&Demangler::ParseArgumentsAtTopLevel);
-    parse_func_ = &Demangler::ParseFunctionTemplateArguments;
-    return name + 1;
-  }
-  parse_func_ = &Demangler::ParseArgumentsAtTopLevel;
-  return name;
-}
-
-const char* Demangler::ParseArgumentsAtTopLevel(const char* name) {
-  // At the top level is the only place where T is allowed.
-  if (*name == 'T') {
-    name++;
-    name = ParseT(name);
-    if (name == nullptr) {
-      return nullptr;
-    }
-    AppendArgument(cur_state_.str);
-    cur_state_.str.clear();
-    return name;
-  }
-
-  return Demangler::ParseArguments(name);
-}
-
-std::string Demangler::Parse(const char* name, size_t max_length) {
-  if (name[0] == '\0' || name[0] != '_' || name[1] == '\0' || name[1] != 'Z') {
-    // Name is not mangled.
-    return name;
-  }
-
-  Clear();
-
-  parse_func_ = &Demangler::FindFunctionName;
-  parse_funcs_.push_back(&Demangler::Fail);
-  const char* cur_name = name + 2;
-  while (cur_name != nullptr && *cur_name != '\0'
-      && static_cast<size_t>(cur_name - name) < max_length) {
-    cur_name = (this->*parse_func_)(cur_name);
-  }
-  if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() ||
-      !cur_state_.suffixes.empty()) {
-    return name;
-  }
-
-  std::string return_type;
-  if (template_found_) {
-    // Only a single argument with a template is not allowed.
-    if (cur_state_.args.size() == 1) {
-      return name;
-    }
-
-    // If there are at least two arguments, this template has a return type.
-    if (cur_state_.args.size() > 1) {
-      // The first argument will be the return value.
-      return_type = cur_state_.args[0] + ' ';
-      cur_state_.args.erase(cur_state_.args.begin());
-    }
-  }
-
-  std::string arg_str;
-  if (cur_state_.args.size() == 1 && cur_state_.args[0] == "void") {
-    // If the only argument is void, then don't print any args.
-    arg_str = "()";
-  } else {
-    arg_str = GetArgumentsString();
-    if (!arg_str.empty()) {
-      arg_str = '(' + arg_str + ')';
-    }
-  }
-  return return_type + function_name_ + arg_str + function_suffix_;
-}
-
-std::string demangle(const char* name) {
-  Demangler demangler;
-  return demangler.Parse(name);
-}
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
deleted file mode 100644
index 3b7d44e..0000000
--- a/demangle/Demangler.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LIB_DEMANGLE_DEMANGLER_H
-#define __LIB_DEMANGLE_DEMANGLER_H
-
-#include <assert.h>
-
-#include <stack>
-#include <string>
-#include <vector>
-
-class Demangler {
- public:
-  Demangler() = default;
-
-  // NOTE: The max_length is not guaranteed to be the absolute max length
-  // of a string that will be rejected. Under certain circumstances the
-  // length check will not occur until after the second letter of a pair
-  // is checked.
-  std::string Parse(const char* name, size_t max_length = kMaxDefaultLength);
-
-  void AppendCurrent(const std::string& str);
-  void AppendCurrent(const char* str);
-  void AppendArgument(const std::string& str);
-  std::string GetArgumentsString();
-  void FinalizeTemplate();
-  const char* ParseS(const char* name);
-  const char* ParseT(const char* name);
-  const char* AppendOperatorString(const char* name);
-  void Save(const std::string& str, bool is_name);
-
- private:
-  void Clear() {
-    parse_funcs_.clear();
-    function_name_.clear();
-    function_suffix_.clear();
-    first_save_.clear();
-    cur_state_.Clear();
-    saves_.clear();
-    template_saves_.clear();
-    while (!state_stack_.empty()) {
-      state_stack_.pop();
-    }
-    last_save_name_ = false;
-    template_found_ = false;
-  }
-
-  using parse_func_type = const char* (Demangler::*)(const char*);
-  parse_func_type parse_func_;
-  std::vector<parse_func_type> parse_funcs_;
-  std::vector<std::string> saves_;
-  std::vector<std::string> template_saves_;
-  bool last_save_name_;
-  bool template_found_;
-
-  std::string function_name_;
-  std::string function_suffix_;
-
-  struct StateData {
-    void Clear() {
-      str.clear();
-      args.clear();
-      prefix.clear();
-      suffixes.clear();
-      last_save.clear();
-    }
-
-    std::string str;
-    std::vector<std::string> args;
-    std::string prefix;
-    std::vector<std::string> suffixes;
-    std::string last_save;
-  };
-  std::stack<StateData> state_stack_;
-  std::string first_save_;
-  StateData cur_state_;
-
-  static const char* GetStringFromLength(const char* name, std::string* str);
-
-  // Parsing functions.
-  const char* ParseComplexString(const char* name);
-  const char* ParseComplexArgument(const char* name);
-  const char* ParseArgumentsAtTopLevel(const char* name);
-  const char* ParseArguments(const char* name);
-  const char* ParseTemplateArguments(const char* name);
-  const char* ParseTemplateArgumentsComplex(const char* name);
-  const char* ParseTemplateLiteral(const char* name);
-  const char* ParseFunctionArgument(const char* name);
-  const char* ParseFunctionName(const char* name);
-  const char* ParseFunctionNameTemplate(const char* name);
-  const char* ParseFunctionTemplateArguments(const char* name);
-  const char* FindFunctionName(const char* name);
-  const char* Fail(const char*) { return nullptr; }
-
-  // The default maximum string length string to process.
-  static constexpr size_t kMaxDefaultLength = 2048;
-
-  static constexpr const char* kTypes[] = {
-    "signed char",        // a
-    "bool",               // b
-    "char",               // c
-    "double",             // d
-    "long double",        // e
-    "float",              // f
-    "__float128",         // g
-    "unsigned char",      // h
-    "int",                // i
-    "unsigned int",       // j
-    nullptr,              // k
-    "long",               // l
-    "unsigned long",      // m
-    "__int128",           // n
-    "unsigned __int128",  // o
-    nullptr,              // p
-    nullptr,              // q
-    nullptr,              // r
-    "short",              // s
-    "unsigned short",     // t
-    nullptr,              // u
-    "void",               // v
-    "wchar_t",            // w
-    "long long",          // x
-    "unsigned long long", // y
-    "...",                // z
-  };
-
-  static constexpr const char* kDTypes[] = {
-    "auto",               // a
-    nullptr,              // b
-    nullptr,              // c
-    "decimal64",          // d
-    "decimal128",         // e
-    "decimal32",          // f
-    nullptr,              // g
-    "half",               // h
-    "char32_t",           // i
-    nullptr,              // j
-    nullptr,              // k
-    nullptr,              // l
-    nullptr,              // m
-    "decltype(nullptr)",  // n
-    nullptr,              // o
-    nullptr,              // p
-    nullptr,              // q
-    nullptr,              // r
-    "char16_t",           // s
-    nullptr,              // t
-    nullptr,              // u
-    nullptr,              // v
-    nullptr,              // w
-    nullptr,              // x
-    nullptr,              // y
-    nullptr,              // z
-  };
-
-  static constexpr const char* kSTypes[] = {
-    "std::allocator",     // a
-    "std::basic_string",  // b
-    nullptr,              // c
-    "std::iostream",      // d
-    nullptr,              // e
-    nullptr,              // f
-    nullptr,              // g
-    nullptr,              // h
-    "std::istream",       // i
-    nullptr,              // j
-    nullptr,              // k
-    nullptr,              // l
-    nullptr,              // m
-    nullptr,              // n
-    "std::ostream",       // o
-    nullptr,              // p
-    nullptr,              // q
-    nullptr,              // r
-    "std::string",        // s
-    nullptr,              // t
-    nullptr,              // u
-    nullptr,              // v
-    nullptr,              // w
-    nullptr,              // x
-    nullptr,              // y
-    nullptr,              // z
-  };
-};
-
-#endif  // __LIB_DEMANGLE_DEMANGLER_H
diff --git a/demangle/OWNERS b/demangle/OWNERS
deleted file mode 100644
index 6f7e4a3..0000000
--- a/demangle/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-cferris@google.com
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
deleted file mode 100644
index 66e5e58..0000000
--- a/demangle/demangle.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <cctype>
-#include <string>
-
-#include <demangle.h>
-
-extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
-
-static void Usage(const char* prog_name) {
-  printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
-  printf("\n");
-  printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
-  printf("reading from stdin otherwise.\n");
-  printf("\n");
-  printf("-c\tCompare against __cxa_demangle\n");
-  printf("\n");
-}
-
-static std::string DemangleWithCxa(const char* name) {
-  const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
-  if (cxa_demangle == nullptr) {
-    return name;
-  }
-
-  // The format of our demangler is slightly different from the cxa demangler
-  // so modify the cxa demangler output. Specifically, for templates, remove
-  // the spaces between '>' and '>'.
-  std::string demangled_str;
-  for (size_t i = 0; i < strlen(cxa_demangle); i++) {
-    if (i > 2 && cxa_demangle[i] == '>' && std::isspace(cxa_demangle[i - 1]) &&
-        cxa_demangle[i - 2] == '>') {
-      demangled_str.resize(demangled_str.size() - 1);
-    }
-    demangled_str += cxa_demangle[i];
-  }
-  return demangled_str;
-}
-
-static void Compare(const char* name, const std::string& demangled_name) {
-  std::string cxa_demangled_name(DemangleWithCxa(name));
-  if (cxa_demangled_name != demangled_name) {
-    printf("\nMismatch!\n");
-    printf("\tmangled name: %s\n", name);
-    printf("\tour demangle: %s\n", demangled_name.c_str());
-    printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
-    exit(1);
-  }
-}
-
-static int Filter(bool compare) {
-  char* line = nullptr;
-  size_t line_length = 0;
-
-  while ((getline(&line, &line_length, stdin)) != -1) {
-    char* p = line;
-    char* name;
-    while ((name = strstr(p, "_Z")) != nullptr) {
-      // Output anything before the identifier.
-      *name = 0;
-      printf("%s", p);
-      *name = '_';
-
-      // Extract the identifier.
-      p = name;
-      while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
-
-      // Demangle and output.
-      std::string identifier(name, p);
-      std::string demangled_name = demangle(identifier.c_str());
-      printf("%s", demangled_name.c_str());
-
-      if (compare) Compare(identifier.c_str(), demangled_name);
-    }
-    // Output anything after the last identifier.
-    printf("%s", p);
-  }
-
-  free(line);
-  return 0;
-}
-
-int main(int argc, char** argv) {
-#ifdef __BIONIC__
-  const char* prog_name = getprogname();
-#else
-  const char* prog_name = argv[0];
-#endif
-
-  bool compare = false;
-  int opt_char;
-  while ((opt_char = getopt(argc, argv, "c")) != -1) {
-    if (opt_char == 'c') {
-      compare = true;
-    } else {
-      Usage(prog_name);
-      return 1;
-    }
-  }
-
-  // With no arguments, act as a filter.
-  if (optind == argc) return Filter(compare);
-
-  // Otherwise demangle each argument.
-  while (optind < argc) {
-    const char* name = argv[optind++];
-    std::string demangled_name = demangle(name);
-    printf("%s\n", demangled_name.c_str());
-
-    if (compare) Compare(name, demangled_name);
-  }
-  return 0;
-}
diff --git a/demangle/demangle_fuzzer.cpp b/demangle/demangle_fuzzer.cpp
deleted file mode 100644
index 83fafc2..0000000
--- a/demangle/demangle_fuzzer.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include "Demangler.h"
-
-extern "C" void LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  std::vector<char> data_str(size + 1);
-  memcpy(data_str.data(), data, size);
-  data_str[size] = '\0';
-
-  Demangler demangler;
-  std::string demangled_name = demangler.Parse(data_str.data());
-  if (size != 0 && data_str[0] != '\0' && demangled_name.empty()) {
-    abort();
-  }
-}
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 2ebd57d..b3f2d5f 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -56,12 +56,20 @@
     if (!path) {
         return false;
     }
+
+    CreateLogicalPartitionParams params = {
+            .block_device = *path,
+            .metadata_slot = slot_number,
+            .partition_name = partition_name,
+            .force_writable = true,
+            .timeout_ms = 5s,
+    };
     std::string dm_path;
-    if (!CreateLogicalPartition(path->c_str(), slot_number, partition_name, true, 5s, &dm_path)) {
+    if (!CreateLogicalPartition(params, &dm_path)) {
         LOG(ERROR) << "Could not map partition: " << partition_name;
         return false;
     }
-    auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name, 5s); };
+    auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name); };
     *handle = PartitionHandle(dm_path, std::move(closer));
     return true;
 }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 39abc4a..8923f40 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -144,14 +144,13 @@
     { "dts",      "dt.img",           "dt.sig",       "dts",      true,  ImageType::BootCritical },
     { "odm",      "odm.img",          "odm.sig",      "odm",      true,  ImageType::Normal },
     { "product",  "product.img",      "product.sig",  "product",  true,  ImageType::Normal },
-    { "product_services",
-                  "product_services.img",
-                                      "product_services.sig",
-                                                      "product_services",
-                                                                  true,  ImageType::Normal },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  ImageType::BootCritical },
     { "super",    "super.img",        "super.sig",    "super",    true,  ImageType::Extra },
     { "system",   "system.img",       "system.sig",   "system",   false, ImageType::Normal },
+    { "system_ext",
+                  "system_ext.img",   "system_ext.sig",
+                                                      "system_ext",
+                                                                  true,  ImageType::Normal },
     { nullptr,    "system_other.img", "system.sig",   "system",   true,  ImageType::Normal },
     { "userdata", "userdata.img",     "userdata.sig", "userdata", true,  ImageType::Extra },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  ImageType::BootCritical },
@@ -389,6 +388,8 @@
             " set_active SLOT            Set the active slot.\n"
             " oem [COMMAND...]           Execute OEM-specific command.\n"
             " gsi wipe|disable           Wipe or disable a GSI installation (fastbootd only).\n"
+            " wipe-super [SUPER_EMPTY]   Wipe the super partition. This will reset it to\n"
+            "                            contain an empty set of default dynamic partitions.\n"
             "\n"
             "boot image:\n"
             " boot KERNEL [RAMDISK [SECOND]]\n"
@@ -1582,6 +1583,76 @@
     return false;
 }
 
+static bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,
+                       std::string* message) {
+    auto super_device = GetMetadataSuperBlockDevice(metadata);
+    auto block_size = metadata.geometry.logical_block_size;
+    auto super_bdev_name = android::fs_mgr::GetBlockDevicePartitionName(*super_device);
+
+    if (super_bdev_name != "super") {
+        // retrofit devices do not allow flashing to the retrofit partitions,
+        // so enable it if we can.
+        fb->RawCommand("oem allow-flash-super");
+    }
+
+    // Note: do not use die() in here, since we want TemporaryDir's destructor
+    // to be called.
+    TemporaryDir temp_dir;
+
+    bool ok;
+    if (metadata.block_devices.size() > 1) {
+        ok = WriteSplitImageFiles(temp_dir.path, metadata, block_size, {}, true);
+    } else {
+        auto image_path = temp_dir.path + "/"s + super_bdev_name + ".img";
+        ok = WriteToImageFile(image_path, metadata, block_size, {}, true);
+    }
+    if (!ok) {
+        *message = "Could not generate a flashable super image file";
+        return false;
+    }
+
+    for (const auto& block_device : metadata.block_devices) {
+        auto partition = android::fs_mgr::GetBlockDevicePartitionName(block_device);
+        bool force_slot = !!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED);
+
+        std::string image_name;
+        if (metadata.block_devices.size() > 1) {
+            image_name = "super_" + partition + ".img";
+        } else {
+            image_name = partition + ".img";
+        }
+
+        auto image_path = temp_dir.path + "/"s + image_name;
+        auto flash = [&](const std::string& partition_name) {
+            do_flash(partition_name.c_str(), image_path.c_str());
+        };
+        do_for_partitions(partition, slot, flash, force_slot);
+
+        unlink(image_path.c_str());
+    }
+    return true;
+}
+
+static void do_wipe_super(const std::string& image, const std::string& slot_override) {
+    if (access(image.c_str(), R_OK) != 0) {
+        die("Could not read image: %s", image.c_str());
+    }
+    auto metadata = android::fs_mgr::ReadFromImageFile(image);
+    if (!metadata) {
+        die("Could not parse image: %s", image.c_str());
+    }
+
+    auto slot = slot_override;
+    if (slot.empty()) {
+        slot = get_current_slot();
+    }
+
+    std::string message;
+    if (!wipe_super(*metadata.get(), slot, &message)) {
+        die(message);
+    }
+}
+
 int FastBootTool::Main(int argc, char* argv[]) {
     bool wants_wipe = false;
     bool wants_reboot = false;
@@ -1958,6 +2029,14 @@
             } else {
                 syntax_error("expected 'wipe' or 'disable'");
             }
+        } else if (command == "wipe-super") {
+            std::string image;
+            if (args.empty()) {
+                image = find_item_given_name("super_empty.img");
+            } else {
+                image = next_arg(&args);
+            }
+            do_wipe_super(image, slot_override);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index d02b37f..900d6ea 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -53,6 +53,10 @@
     exit(EXIT_FAILURE);
 }
 
+void die(const std::string& str) {
+    die("%s", str.c_str());
+}
+
 void set_verbose() {
     g_verbose = true;
 }
diff --git a/fastboot/util.h b/fastboot/util.h
index 2535414..c719df2 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -15,4 +15,7 @@
 // use the same attribute for compile-time format string checking.
 void die(const char* fmt, ...) __attribute__((__noreturn__))
 __attribute__((__format__(__printf__, 1, 2)));
+
 void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
+
+void die(const std::string& str) __attribute__((__noreturn__));
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3d3503c..65f0eff 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -73,6 +73,7 @@
     whole_static_libs: [
         "liblogwrap",
         "libdm",
+        "libext2_uuid",
         "libfstab",
     ],
     cppflags: [
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index d204bfd..fe2e052 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -95,8 +95,8 @@
 - Kernel must have CONFIG_OVERLAY_FS=y and will need to be patched
   with "*overlayfs: override_creds=off option bypass creator_cred*"
   if kernel is 4.4 or higher.
-  The patch is available on the upstream mailing list and the latest as of
-  Feb 8 2019 is https://lore.kernel.org/patchwork/patch/1009299/.
+  The patch series is available on the upstream mailing list and the latest as
+  of Jul 24 2019 is https://lore.kernel.org/patchwork/patch/1104577/.
   This patch adds an override_creds _mount_ option to overlayfs that
   permits legacy behavior for systems that do not have overlapping
   sepolicy rules, principals of least privilege, which is how Android behaves.
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 259f800..7a0d019 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1331,7 +1331,7 @@
                 continue;
             }
         } else if ((current_entry.fs_mgr_flags.verify)) {
-            if (!fs_mgr_teardown_verity(&current_entry, true /* wait */)) {
+            if (!fs_mgr_teardown_verity(&current_entry)) {
                 LERROR << "Failed to tear down verified partition on mount point: "
                        << current_entry.mount_point;
                 ret |= FsMgrUmountStatus::ERROR_VERITY;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 1f21a71..fa756a0 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -52,29 +52,36 @@
 using DmTargetZero = android::dm::DmTargetZero;
 using DmTargetLinear = android::dm::DmTargetLinear;
 
-static bool GetPhysicalPartitionDevicePath(const LpMetadata& metadata,
+static bool GetPhysicalPartitionDevicePath(const IPartitionOpener& opener,
+                                           const LpMetadata& metadata,
                                            const LpMetadataBlockDevice& block_device,
-                                           const std::string& super_device,
-                                           std::string* result) {
-    // Note: device-mapper will not accept symlinks, so we must use realpath
-    // here.
-    std::string name = GetBlockDevicePartitionName(block_device);
-    std::string path = "/dev/block/by-name/" + name;
+                                           const std::string& super_device, std::string* result) {
     // If the super device is the source of this block device's metadata,
     // make sure we use the correct super device (and not just "super",
     // which might not exist.)
+    std::string name = GetBlockDevicePartitionName(block_device);
+    std::string dev_string = opener.GetDeviceString(name);
     if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
-        path = super_device;
+        dev_string = opener.GetDeviceString(super_device);
     }
-    if (!android::base::Realpath(path, result)) {
-        PERROR << "realpath: " << path;
-        return false;
+
+    // Note: device-mapper will not accept symlinks, so we must use realpath
+    // here. If the device string is a major:minor sequence, we don't need to
+    // to call Realpath (it would not work anyway).
+    if (android::base::StartsWith(dev_string, "/")) {
+        if (!android::base::Realpath(dev_string, result)) {
+            PERROR << "realpath: " << dev_string;
+            return false;
+        }
+    } else {
+        *result = dev_string;
     }
     return true;
 }
 
-static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
-                          const std::string& super_device, DmTable* table) {
+static bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
+                          const LpMetadataPartition& partition, const std::string& super_device,
+                          DmTable* table) {
     uint64_t sector = 0;
     for (size_t i = 0; i < partition.num_extents; i++) {
         const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -85,12 +92,13 @@
                 break;
             case LP_TARGET_TYPE_LINEAR: {
                 const auto& block_device = metadata.block_devices[extent.target_source];
-                std::string path;
-                if (!GetPhysicalPartitionDevicePath(metadata, block_device, super_device, &path)) {
+                std::string dev_string;
+                if (!GetPhysicalPartitionDevicePath(opener, metadata, block_device, super_device,
+                                                    &dev_string)) {
                     LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
                     return false;
                 }
-                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
+                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, dev_string,
                                                           extent.target_data);
                 break;
             }
@@ -109,36 +117,6 @@
     return true;
 }
 
-static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
-                                   bool force_writable, const std::chrono::milliseconds& timeout_ms,
-                                   const std::string& super_device, std::string* path) {
-    DeviceMapper& dm = DeviceMapper::Instance();
-
-    DmTable table;
-    if (!CreateDmTable(metadata, partition, super_device, &table)) {
-        return false;
-    }
-    if (force_writable) {
-        table.set_readonly(false);
-    }
-    std::string name = GetPartitionName(partition);
-    if (!dm.CreateDevice(name, table)) {
-        return false;
-    }
-    if (!dm.GetDmDevicePathByName(name, path)) {
-        return false;
-    }
-    if (timeout_ms > std::chrono::milliseconds::zero()) {
-        if (!WaitForFile(*path, timeout_ms)) {
-            DestroyLogicalPartition(name, {});
-            LERROR << "Timed out waiting for device path: " << *path;
-            return false;
-        }
-    }
-    LINFO << "Created logical partition " << name << " on device " << *path;
-    return true;
-}
-
 bool CreateLogicalPartitions(const std::string& block_device) {
     uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
     auto metadata = ReadMetadata(block_device.c_str(), slot);
@@ -155,13 +133,20 @@
 }
 
 bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
+    CreateLogicalPartitionParams params = {
+            .block_device = super_device,
+            .metadata = &metadata,
+    };
     for (const auto& partition : metadata.partitions) {
         if (!partition.num_extents) {
             LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
             continue;
         }
-        std::string path;
-        if (!CreateLogicalPartition(metadata, partition, false, {}, super_device, &path)) {
+
+        params.partition = &partition;
+
+        std::string ignore_path;
+        if (!CreateLogicalPartition(params, &ignore_path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
@@ -169,49 +154,85 @@
     return true;
 }
 
-bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
-                            const std::string& partition_name, bool force_writable,
-                            const std::chrono::milliseconds& timeout_ms, std::string* path) {
-    for (const auto& partition : metadata.partitions) {
-        if (GetPartitionName(partition) == partition_name) {
-            return CreateLogicalPartition(metadata, partition, force_writable, timeout_ms,
-                                          block_device, path);
+bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path) {
+    const LpMetadata* metadata = params.metadata;
+
+    // Read metadata if needed.
+    std::unique_ptr<LpMetadata> local_metadata;
+    if (!metadata) {
+        if (!params.metadata_slot) {
+            LOG(ERROR) << "Either metadata or a metadata slot must be specified.";
+            return false;
+        }
+        auto slot = *params.metadata_slot;
+        if (local_metadata = ReadMetadata(params.block_device, slot); !local_metadata) {
+            LOG(ERROR) << "Could not read partition table for: " << params.block_device;
+            return false;
+        }
+        metadata = local_metadata.get();
+    }
+
+    // Find the partition by name if needed.
+    const LpMetadataPartition* partition = params.partition;
+    if (!partition) {
+        for (const auto& iter : metadata->partitions) {
+            if (GetPartitionName(iter) == params.partition_name) {
+                partition = &iter;
+                break;
+            }
+        }
+        if (!partition) {
+            LERROR << "Could not find any partition with name: " << params.partition_name;
+            return false;
         }
     }
-    LERROR << "Could not find any partition with name: " << partition_name;
-    return false;
-}
 
-bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
-                            const std::string& partition_name, bool force_writable,
-                            const std::chrono::milliseconds& timeout_ms, std::string* path) {
-    auto metadata = ReadMetadata(block_device.c_str(), metadata_slot);
-    if (!metadata) {
-        LOG(ERROR) << "Could not read partition table.";
-        return true;
-    }
-    return CreateLogicalPartition(block_device, *metadata.get(), partition_name, force_writable,
-                                  timeout_ms, path);
-}
+    PartitionOpener default_opener;
+    const IPartitionOpener* opener =
+            params.partition_opener ? params.partition_opener : &default_opener;
 
-bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
-    DeviceMapper& dm = DeviceMapper::Instance();
-    std::string path;
-    if (timeout_ms > std::chrono::milliseconds::zero()) {
-        dm.GetDmDevicePathByName(name, &path);
-    }
-    if (!dm.DeleteDevice(name)) {
+    DmTable table;
+    if (!CreateDmTable(*opener, *metadata, *partition, params.block_device, &table)) {
         return false;
     }
-    if (!path.empty() && !WaitForFileDeleted(path, timeout_ms)) {
-        LERROR << "Timed out waiting for device path to unlink: " << path;
+    if (params.force_writable) {
+        table.set_readonly(false);
+    }
+
+    std::string device_name = params.device_name;
+    if (device_name.empty()) {
+        device_name = GetPartitionName(*partition);
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.CreateDevice(device_name, table, path, params.timeout_ms)) {
+        return false;
+    }
+    LINFO << "Created logical partition " << device_name << " on device " << *path;
+    return true;
+}
+
+std::string CreateLogicalPartitionParams::GetDeviceName() const {
+    if (!device_name.empty()) return device_name;
+    return GetPartitionName();
+}
+
+std::string CreateLogicalPartitionParams::GetPartitionName() const {
+    if (!partition_name.empty()) return partition_name;
+    if (partition) return android::fs_mgr::GetPartitionName(*partition);
+    return "<unknown partition>";
+}
+
+bool UnmapDevice(const std::string& name) {
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.DeleteDevice(name)) {
         return false;
     }
     return true;
 }
 
-bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
-    if (!UnmapDevice(name, timeout_ms)) {
+bool DestroyLogicalPartition(const std::string& name) {
+    if (!UnmapDevice(name)) {
         return false;
     }
     LINFO << "Unmapped logical partition " << name;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 31790b1..bc197cd 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -46,7 +46,7 @@
 namespace fs_mgr {
 namespace {
 
-const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
+constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
 
 struct FlagList {
     const char *name;
@@ -704,10 +704,9 @@
     return true;
 }
 
-// For GSI to skip mounting /product and /product_services, until there are
-// well-defined interfaces between them and /system. Otherwise, the GSI flashed
-// on /system might not be able to work with /product and /product_services.
-// When they're skipped here, /system/product and /system/product_services in
+// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
+// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
+// /product and /system_ext. When they're skipped here, /system/product and /system/system_ext in
 // GSI will be used.
 bool SkipMountingPartitions(Fstab* fstab) {
     constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 05ca5fc..d7ea81d 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -32,7 +32,6 @@
 #include <unistd.h>
 
 #include <algorithm>
-#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -134,7 +133,7 @@
     return ret | !rmdir(test_directory.c_str());
 }
 
-// At less than 1% free space return value of false,
+// At less than 1% or 8MB of free space return value of false,
 // means we will try to wrap with overlayfs.
 bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
     // If we have access issues to find out space remaining, return true
@@ -146,9 +145,36 @@
         return true;
     }
 
-    static constexpr int kPercentThreshold = 1;  // 1%
+    static constexpr int kPercentThreshold = 1;                       // 1%
+    static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024;  // 8MB
 
-    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
+    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
+           (vst.f_bfree * vst.f_bsize) >= kSizeThreshold;
+}
+
+const auto kPhysicalDevice = "/dev/block/by-name/"s;
+
+bool fs_mgr_update_blk_device(FstabEntry* entry) {
+    if (entry->fs_mgr_flags.logical) {
+        fs_mgr_update_logical_partition(entry);
+    }
+    if (fs_mgr_access(entry->blk_device)) {
+        return true;
+    }
+    if (entry->blk_device != "/dev/root") {
+        return false;
+    }
+
+    // special case for system-as-root (taimen and others)
+    auto blk_device = kPhysicalDevice + "system";
+    if (!fs_mgr_access(blk_device)) {
+        blk_device += fs_mgr_get_slot_suffix();
+        if (!fs_mgr_access(blk_device)) {
+            return false;
+        }
+    }
+    entry->blk_device = blk_device;
+    return true;
 }
 
 bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
@@ -158,19 +184,19 @@
     if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
         return true;
     }
-    if (entry->fs_mgr_flags.logical) {
-        fs_mgr_update_logical_partition(entry);
+
+    // blk_device needs to be setup so we can check superblock.
+    // If we fail here, because during init first stage and have doubts.
+    if (!fs_mgr_update_blk_device(entry)) {
+        return true;
     }
+
+    // check if ext4 de-dupe
     auto save_errno = errno;
-    errno = 0;
     auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
     if (!has_shared_blocks && (entry->mount_point == "/system")) {
         has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
     }
-    // special case for first stage init for system as root (taimen)
-    if (!has_shared_blocks && (errno == ENOENT) && (entry->blk_device == "/dev/root")) {
-        has_shared_blocks = true;
-    }
     errno = save_errno;
     return has_shared_blocks;
 }
@@ -389,8 +415,6 @@
     return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
 }
 
-const auto kPhysicalDevice = "/dev/block/by-name/"s;
-
 std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
     return kPhysicalDevice + fs_mgr_get_super_partition_name(slot_number);
 }
@@ -445,7 +469,7 @@
     auto metadata = builder->Export();
     if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
         if (change) *change = true;
-        if (!DestroyLogicalPartition(partition_name, 0s)) return false;
+        if (!DestroyLogicalPartition(partition_name)) return false;
     } else {
         LERROR << "delete partition " << overlay;
         return false;
@@ -518,10 +542,166 @@
     return ret;
 }
 
+bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {
+    auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,
+                     nullptr);
+    if (ret) {
+        PERROR << "__mount(target=" << mount_point
+               << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
+        return false;
+    }
+    return true;
+}
+
+bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {
+    auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);
+    if (ret) {
+        PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret;
+        return false;
+    }
+    return true;
+}
+
+struct mount_info {
+    std::string mount_point;
+    bool shared_flag;
+};
+
+std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {
+    std::vector<mount_info> info;
+
+    auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (!file) {
+        PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+        return info;
+    }
+
+    ssize_t len;
+    size_t alloc_len = 0;
+    char* line = nullptr;
+    while ((len = getline(&line, &alloc_len, file.get())) != -1) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+
+        static constexpr char delim[] = " \t";
+        char* save_ptr;
+        if (!strtok_r(line, delim, &save_ptr)) {
+            LERROR << "Error parsing mount ID";
+            break;
+        }
+        if (!strtok_r(nullptr, delim, &save_ptr)) {
+            LERROR << "Error parsing parent ID";
+            break;
+        }
+        if (!strtok_r(nullptr, delim, &save_ptr)) {
+            LERROR << "Error parsing mount source";
+            break;
+        }
+        if (!strtok_r(nullptr, delim, &save_ptr)) {
+            LERROR << "Error parsing root";
+            break;
+        }
+
+        char* p;
+        if (!(p = strtok_r(nullptr, delim, &save_ptr))) {
+            LERROR << "Error parsing mount_point";
+            break;
+        }
+        mount_info entry = {p, false};
+
+        if (!strtok_r(nullptr, delim, &save_ptr)) {
+            LERROR << "Error parsing mount_flags";
+            break;
+        }
+
+        while ((p = strtok_r(nullptr, delim, &save_ptr))) {
+            if ((p[0] == '-') && (p[1] == '\0')) break;
+            if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true;
+        }
+        if (!p) {
+            LERROR << "Error parsing fields";
+            break;
+        }
+        info.emplace_back(std::move(entry));
+    }
+
+    free(line);
+    if (info.empty()) {
+        LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'";
+    }
+    return info;
+}
+
 bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
     auto options = fs_mgr_get_overlayfs_options(mount_point);
     if (options.empty()) return false;
 
+    auto retval = true;
+    auto save_errno = errno;
+
+    struct move_entry {
+        std::string mount_point;
+        std::string dir;
+        bool shared_flag;
+    };
+    std::vector<move_entry> move;
+    auto parent_private = false;
+    auto parent_made_private = false;
+    auto dev_private = false;
+    auto dev_made_private = false;
+    for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
+        if ((entry.mount_point == mount_point) && !entry.shared_flag) {
+            parent_private = true;
+        }
+        if ((entry.mount_point == "/dev") && !entry.shared_flag) {
+            dev_private = true;
+        }
+
+        if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
+            continue;
+        }
+        if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
+                return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
+            }) != move.end()) {
+            continue;
+        }
+
+        // use as the bound directory in /dev.
+        auto new_context = fs_mgr_get_context(entry.mount_point);
+        if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
+            PERROR << "setfscreatecon " << new_context;
+        }
+        move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
+                                entry.shared_flag};
+        const auto target = mkdtemp(new_entry.dir.data());
+        if (!target) {
+            retval = false;
+            save_errno = errno;
+            PERROR << "temporary directory for MS_BIND";
+            setfscreatecon(nullptr);
+            continue;
+        }
+        setfscreatecon(nullptr);
+
+        if (!parent_private && !parent_made_private) {
+            parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+        }
+        if (new_entry.shared_flag) {
+            new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
+        }
+        if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
+            retval = false;
+            save_errno = errno;
+            if (new_entry.shared_flag) {
+                fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
+            }
+            continue;
+        }
+        move.emplace_back(std::move(new_entry));
+    }
+
     // hijack __mount() report format to help triage
     auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
     const auto opt_list = android::base::Split(options, ",");
@@ -536,12 +716,38 @@
     auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
                      options.c_str());
     if (ret) {
+        retval = false;
+        save_errno = errno;
         PERROR << report << ret;
-        return false;
     } else {
         LINFO << report << ret;
-        return true;
     }
+
+    // Move submounts back.
+    for (const auto& entry : move) {
+        if (!dev_private && !dev_made_private) {
+            dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
+        }
+
+        if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
+            retval = false;
+            save_errno = errno;
+        } else if (entry.shared_flag &&
+                   !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
+            retval = false;
+            save_errno = errno;
+        }
+        rmdir(entry.dir.c_str());
+    }
+    if (dev_made_private) {
+        fs_mgr_overlayfs_set_shared_mount("/dev", true);
+    }
+    if (parent_made_private) {
+        fs_mgr_overlayfs_set_shared_mount(mount_point, true);
+    }
+
+    errno = save_errno;
+    return retval;
 }
 
 // Mount kScratchMountPoint
@@ -663,7 +869,7 @@
             if (!android::base::EndsWith(name, suffix)) {
                 continue;
             }
-            if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name, 2s)) {
+            if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {
                 continue;
             }
             builder->ResizePartition(builder->FindPartition(name), 0);
@@ -737,7 +943,7 @@
                         return false;
                     }
                 }
-                if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
+                if (!partition_create) DestroyLogicalPartition(partition_name);
                 changed = true;
                 *partition_exists = false;
             }
@@ -755,9 +961,16 @@
     }
 
     if (changed || partition_create) {
-        if (!CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
-                                    scratch_device))
+        CreateLogicalPartitionParams params = {
+                .block_device = super_device,
+                .metadata_slot = slot_number,
+                .partition_name = partition_name,
+                .force_writable = true,
+                .timeout_ms = 10s,
+        };
+        if (!CreateLogicalPartition(params, scratch_device)) {
             return false;
+        }
 
         if (change) *change = true;
     }
@@ -978,11 +1191,15 @@
     if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         auto scratch_device = fs_mgr_overlayfs_scratch_device();
         if (scratch_device.empty()) {
-            auto slot_number = fs_mgr_overlayfs_slot_number();
-            auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-            const auto partition_name = android::base::Basename(kScratchMountPoint);
-            CreateLogicalPartition(super_device, slot_number, partition_name, true, 10s,
-                                   &scratch_device);
+            auto metadata_slot = fs_mgr_overlayfs_slot_number();
+            CreateLogicalPartitionParams params = {
+                    .block_device = fs_mgr_overlayfs_super_device(metadata_slot),
+                    .metadata_slot = metadata_slot,
+                    .partition_name = android::base::Basename(kScratchMountPoint),
+                    .force_writable = true,
+                    .timeout_ms = 10s,
+            };
+            CreateLogicalPartition(params, &scratch_device);
         }
         mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
                                                        fs_mgr_overlayfs_scratch_mount_type());
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 3a33cf3..c5e477c 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -97,10 +97,10 @@
 bool fs_mgr_is_ext4(const std::string& blk_device);
 bool fs_mgr_is_f2fs(const std::string& blk_device);
 
-bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab, bool wait);
+bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
 
 namespace android {
 namespace fs_mgr {
-bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+bool UnmapDevice(const std::string& name);
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 149bee3..93bba68 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -82,13 +82,7 @@
 
 void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
               const char* file, unsigned int line, const char* message) {
-    static const char log_characters[] = "VD\0WEFF";
-    if (severity < sizeof(log_characters)) {
-        auto severity_char = log_characters[severity];
-        if (severity_char) fprintf(stderr, "%c ", severity_char);
-    }
     fprintf(stderr, "%s\n", message);
-
     static auto logd = android::base::LogdLogger();
     logd(id, severity, tag, file, line, message);
 }
@@ -107,11 +101,9 @@
 
 }  // namespace
 
-int main(int argc, char* argv[]) {
-    android::base::InitLogging(argv, MyLogger);
-
+static int do_remount(int argc, char* argv[]) {
     enum {
-        SUCCESS,
+        SUCCESS = 0,
         NOT_USERDEBUG,
         BADARG,
         NOT_ROOT,
@@ -165,7 +157,7 @@
 
     // Make sure we are root.
     if (::getuid() != 0) {
-        LOG(ERROR) << "must be run as root";
+        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
         return NOT_ROOT;
     }
 
@@ -390,3 +382,10 @@
 
     return retval;
 }
+
+int main(int argc, char* argv[]) {
+    android::base::InitLogging(argv, MyLogger);
+    int result = do_remount(argc, argv);
+    printf("remount %s\n", result ? "failed" : "succeeded");
+    return result;
+}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index be8077b..efa2180 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -547,9 +547,9 @@
     return retval;
 }
 
-bool fs_mgr_teardown_verity(FstabEntry* entry, bool wait) {
+bool fs_mgr_teardown_verity(FstabEntry* entry) {
     const std::string mount_point(basename(entry->mount_point.c_str()));
-    if (!android::fs_mgr::UnmapDevice(mount_point, wait ? 1000ms : 0ms)) {
+    if (!android::fs_mgr::UnmapDevice(mount_point)) {
         return false;
     }
     LINFO << "Unmapped verity device " << mount_point;
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index f33fc02..2054fa1 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -29,6 +29,7 @@
 
 #include <chrono>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -49,26 +50,44 @@
 // method for ReadMetadata and CreateLogicalPartitions.
 bool CreateLogicalPartitions(const std::string& block_device);
 
-// Create a block device for a single logical partition, given metadata and
-// the partition name. On success, a path to the partition's block device is
-// returned. If |force_writable| is true, the "readonly" flag will be ignored
-// so the partition can be flashed.
-//
-// If |timeout_ms| is non-zero, then CreateLogicalPartition will block for the
-// given amount of time until the path returned in |path| is available.
-bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
-                            const std::string& partition_name, bool force_writable,
-                            const std::chrono::milliseconds& timeout_ms, std::string* path);
+struct CreateLogicalPartitionParams {
+    // Block device of the super partition.
+    std::string block_device;
 
-// Same as above, but with a given metadata object. Care should be taken that
-// the metadata represents a valid partition layout.
-bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
-                            const std::string& partition_name, bool force_writable,
-                            const std::chrono::milliseconds& timeout_ms, std::string* path);
+    // If |metadata| is null, the slot will be read using |metadata_slot|.
+    const LpMetadata* metadata = nullptr;
+    std::optional<uint32_t> metadata_slot;
+
+    // If |partition| is not set, it will be found via |partition_name|.
+    const LpMetadataPartition* partition = nullptr;
+    std::string partition_name;
+
+    // Force the device to be read-write even if it was specified as readonly
+    // in the metadata.
+    bool force_writable = false;
+
+    // If |timeout_ms| is non-zero, then CreateLogicalPartition will block for
+    // the given amount of time until the path returned in |path| is available.
+    std::chrono::milliseconds timeout_ms = {};
+
+    // If this is non-empty, it will override the device mapper name (by
+    // default the partition name will be used).
+    std::string device_name;
+
+    // If non-null, this will use the specified IPartitionOpener rather than
+    // the default one.
+    const IPartitionOpener* partition_opener = nullptr;
+
+    // Helpers for determining the effective partition and device name.
+    std::string GetPartitionName() const;
+    std::string GetDeviceName() const;
+};
+
+bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path);
 
 // Destroy the block device for a logical partition, by name. If |timeout_ms|
 // is non-zero, then this will block until the device path has been unlinked.
-bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+bool DestroyLogicalPartition(const std::string& name);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 21255df..4cdea71 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -27,8 +27,12 @@
         "dm_target.cpp",
         "dm.cpp",
         "loop_control.cpp",
+        "utility.cpp",
     ],
 
+    static_libs: [
+        "libext2_uuid",
+    ],
     header_libs: [
         "libbase_headers",
         "liblog_headers",
@@ -46,6 +50,7 @@
     static_libs: [
         "libdm",
         "libbase",
+        "libext2_uuid",
         "libfs_mgr",
         "liblog",
     ],
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index d54b6ef..e7a3ff2 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,12 +20,22 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 
+#include <functional>
+#include <thread>
+
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <uuid/uuid.h>
+
+#include "utility.h"
 
 namespace android {
 namespace dm {
 
+using namespace std::literals;
+
 DeviceMapper::DeviceMapper() : fd_(-1) {
     fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
     if (fd_ < 0) {
@@ -37,13 +47,13 @@
     static DeviceMapper instance;
     return instance;
 }
+
 // Creates a new device mapper device
-bool DeviceMapper::CreateDevice(const std::string& name) {
+bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
     if (name.empty()) {
         LOG(ERROR) << "Unnamed device mapper device creation is not supported";
         return false;
     }
-
     if (name.size() >= DM_NAME_LEN) {
         LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
         return false;
@@ -51,6 +61,9 @@
 
     struct dm_ioctl io;
     InitIo(&io, name);
+    if (!uuid.empty()) {
+        snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
+    }
 
     if (ioctl(fd_, DM_DEV_CREATE, &io)) {
         PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
@@ -66,17 +79,14 @@
     return true;
 }
 
+bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
+    if (GetState(name) == DmDeviceState::INVALID) {
+        return true;
+    }
+    return DeleteDevice(name);
+}
+
 bool DeviceMapper::DeleteDevice(const std::string& name) {
-    if (name.empty()) {
-        LOG(ERROR) << "Unnamed device mapper device creation is not supported";
-        return false;
-    }
-
-    if (name.size() >= DM_NAME_LEN) {
-        LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
-        return false;
-    }
-
     struct dm_ioctl io;
     InitIo(&io, name);
 
@@ -93,9 +103,67 @@
     return true;
 }
 
-const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
-    // TODO(b/110035986): Return the table, as read from the kernel instead
-    return nullptr;
+static std::string GenerateUuid() {
+    uuid_t uuid_bytes;
+    uuid_generate(uuid_bytes);
+
+    char uuid_chars[37] = {};
+    uuid_unparse_lower(uuid_bytes, uuid_chars);
+
+    return std::string{uuid_chars};
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+                                const std::chrono::milliseconds& timeout_ms) {
+    std::string uuid = GenerateUuid();
+    if (!CreateDevice(name, uuid)) {
+        return false;
+    }
+
+    // We use the unique path for testing whether the device is ready. After
+    // that, it's safe to use the dm-N path which is compatible with callers
+    // that expect it to be formatted as such.
+    std::string unique_path;
+    if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
+        !GetDmDevicePathByName(name, path)) {
+        DeleteDevice(name);
+        return false;
+    }
+
+    if (timeout_ms <= std::chrono::milliseconds::zero()) {
+        return true;
+    }
+    if (!WaitForFile(unique_path, timeout_ms)) {
+        LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
+        DeleteDevice(name);
+        return false;
+    }
+    return true;
+}
+
+bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        PLOG(ERROR) << "Failed to get device path: " << name;
+        return false;
+    }
+
+    if (io.uuid[0] == '\0') {
+        LOG(ERROR) << "Device does not have a unique path: " << name;
+        return false;
+    }
+    *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
+    return true;
+}
+
+std::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        return std::nullopt;
+    }
+    return Info(io.flags);
 }
 
 DmDeviceState DeviceMapper::GetState(const std::string& name) const {
@@ -110,12 +178,27 @@
     return DmDeviceState::SUSPENDED;
 }
 
-bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
-    if (!CreateDevice(name)) {
+bool DeviceMapper::ChangeState(const std::string& name, DmDeviceState state) {
+    if (state != DmDeviceState::SUSPENDED && state != DmDeviceState::ACTIVE) {
         return false;
     }
-    if (!LoadTableAndActivate(name, table)) {
-        DeleteDevice(name);
+
+    struct dm_ioctl io;
+    InitIo(&io, name);
+
+    if (state == DmDeviceState::SUSPENDED) io.flags = DM_SUSPEND_FLAG;
+
+    if (ioctl(fd_, DM_DEV_SUSPEND, &io) < 0) {
+        PLOG(ERROR) << "DM_DEV_SUSPEND "
+                    << (state == DmDeviceState::SUSPENDED ? "suspend" : "resume") << " failed";
+        return false;
+    }
+    return true;
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
+    std::string ignore_path;
+    if (!CreateDevice(name, table, &ignore_path, 0ms)) {
         return false;
     }
     return true;
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index f7497f6..65f6e12 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -16,6 +16,7 @@
 
 #include "libdm/dm_target.h"
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <sys/types.h>
 
@@ -149,6 +150,25 @@
     return base_device_ + " " + cow_device_ + " " + mode + " " + std::to_string(chunk_size_);
 }
 
+// Computes the percentage of complition for snapshot status.
+// @sectors_initial is the number of sectors_allocated stored right before
+// starting the merge.
+double DmTargetSnapshot::MergePercent(const DmTargetSnapshot::Status& status,
+                                      uint64_t sectors_initial) {
+    uint64_t s = status.sectors_allocated;
+    uint64_t t = status.total_sectors;
+    uint64_t m = status.metadata_sectors;
+    uint64_t i = sectors_initial == 0 ? t : sectors_initial;
+
+    if (t <= s || i <= s) {
+        return 0.0;
+    }
+    if (s == 0 || t == 0 || s <= m) {
+        return 100.0;
+    }
+    return 100.0 / (i - m) * (i - s);
+}
+
 bool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {
     DeviceMapper& dm = DeviceMapper::Instance();
     DmTargetTypeInfo info;
@@ -165,34 +185,40 @@
 }
 
 bool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {
+    // Try to parse the line as it should be
+    int args = sscanf(text.c_str(), "%" PRIu64 "/%" PRIu64 " %" PRIu64, &status->sectors_allocated,
+                      &status->total_sectors, &status->metadata_sectors);
+    if (args == 3) {
+        return true;
+    }
     auto sections = android::base::Split(text, " ");
+    if (sections.size() == 0) {
+        LOG(ERROR) << "could not parse empty status";
+        return false;
+    }
+    // Error codes are: "Invalid", "Overflow" and "Merge failed"
     if (sections.size() == 1) {
-        // This is probably an error code, "Invalid" is possible as is "Overflow"
-        // on 4.4+.
+        if (text == "Invalid" || text == "Overflow") {
+            status->error = text;
+            return true;
+        }
+    } else if (sections.size() == 2 && text == "Merge failed") {
         status->error = text;
         return true;
     }
-    if (sections.size() != 2) {
-        LOG(ERROR) << "snapshot status should have two components";
+    LOG(ERROR) << "could not parse snapshot status: wrong format";
+    return false;
+}
+
+bool DmTargetSnapshot::GetDevicesFromParams(const std::string& params, std::string* base_device,
+                                            std::string* cow_device) {
+    auto pieces = android::base::Split(params, " ");
+    if (pieces.size() < 2) {
+        LOG(ERROR) << "Parameter string is invalid: " << params;
         return false;
     }
-    auto sector_info = android::base::Split(sections[0], "/");
-    if (sector_info.size() != 2) {
-        LOG(ERROR) << "snapshot sector info should have two components";
-        return false;
-    }
-    if (!android::base::ParseUint(sections[1], &status->metadata_sectors)) {
-        LOG(ERROR) << "could not parse metadata sectors";
-        return false;
-    }
-    if (!android::base::ParseUint(sector_info[0], &status->sectors_allocated)) {
-        LOG(ERROR) << "could not parse sectors allocated";
-        return false;
-    }
-    if (!android::base::ParseUint(sector_info[1], &status->total_sectors)) {
-        LOG(ERROR) << "could not parse total sectors";
-        return false;
-    }
+    *base_device = pieces[0];
+    *cow_device = pieces[1];
     return true;
 }
 
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index c5881dd..eed21dc 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -52,10 +52,10 @@
   public:
     TempDevice(const std::string& name, const DmTable& table)
         : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
-        valid_ = dm_.CreateDevice(name, table);
+        valid_ = dm_.CreateDevice(name, table, &path_, 5s);
     }
     TempDevice(TempDevice&& other) noexcept
-        : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+        : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
         other.valid_ = false;
     }
     ~TempDevice() {
@@ -70,29 +70,7 @@
         valid_ = false;
         return dm_.DeleteDevice(name_);
     }
-    bool WaitForUdev() const {
-        auto start_time = std::chrono::steady_clock::now();
-        while (true) {
-            if (!access(path().c_str(), F_OK)) {
-                return true;
-            }
-            if (errno != ENOENT) {
-                return false;
-            }
-            std::this_thread::sleep_for(50ms);
-            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start_time;
-            if (elapsed >= 5s) {
-                return false;
-            }
-        }
-    }
-    std::string path() const {
-        std::string device_path;
-        if (!dm_.GetDmDevicePathByName(name_, &device_path)) {
-            return "";
-        }
-        return device_path;
-    }
+    std::string path() const { return path_; }
     const std::string& name() const { return name_; }
     bool valid() const { return valid_; }
 
@@ -109,6 +87,7 @@
   private:
     DeviceMapper& dm_;
     std::string name_;
+    std::string path_;
     bool valid_;
 };
 
@@ -124,9 +103,9 @@
     ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));
     ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));
 
-    LoopDevice loop_a(tmp1);
+    LoopDevice loop_a(tmp1, 10s);
     ASSERT_TRUE(loop_a.valid());
-    LoopDevice loop_b(tmp2);
+    LoopDevice loop_b(tmp2, 10s);
     ASSERT_TRUE(loop_b.valid());
 
     // Define a 2-sector device, with each sector mapping to the first sector
@@ -139,7 +118,6 @@
     TempDevice dev("libdm-test-dm-linear", table);
     ASSERT_TRUE(dev.valid());
     ASSERT_FALSE(dev.path().empty());
-    ASSERT_TRUE(dev.WaitForUdev());
 
     auto& dm = DeviceMapper::Instance();
 
@@ -188,6 +166,34 @@
     ASSERT_TRUE(dev.Destroy());
 }
 
+TEST(libdm, DmSuspendResume) {
+    unique_fd tmp1(CreateTempFile("file_suspend_resume", 512));
+    ASSERT_GE(tmp1, 0);
+
+    LoopDevice loop_a(tmp1, 10s);
+    ASSERT_TRUE(loop_a.valid());
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));
+    ASSERT_TRUE(table.valid());
+
+    TempDevice dev("libdm-test-dm-suspend-resume", table);
+    ASSERT_TRUE(dev.valid());
+    ASSERT_FALSE(dev.path().empty());
+
+    auto& dm = DeviceMapper::Instance();
+
+    // Test Set and Get status of device.
+    vector<DeviceMapper::TargetInfo> targets;
+    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
+
+    ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::SUSPENDED));
+    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::SUSPENDED);
+
+    ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::ACTIVE));
+    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
+}
+
 TEST(libdm, DmVerityArgsAvb2) {
     std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
     std::string algorithm = "sha1";
@@ -277,9 +283,9 @@
     cow_fd_ = CreateTempFile("cow_device", kCowDeviceSize);
     ASSERT_GE(cow_fd_, 0);
 
-    base_loop_ = std::make_unique<LoopDevice>(base_fd_);
+    base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
     ASSERT_TRUE(base_loop_->valid());
-    cow_loop_ = std::make_unique<LoopDevice>(cow_fd_);
+    cow_loop_ = std::make_unique<LoopDevice>(cow_fd_, 10s);
     ASSERT_TRUE(cow_loop_->valid());
 
     DmTable origin_table;
@@ -290,7 +296,6 @@
     origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
     ASSERT_TRUE(origin_dev_->valid());
     ASSERT_FALSE(origin_dev_->path().empty());
-    ASSERT_TRUE(origin_dev_->WaitForUdev());
 
     // chunk size = 4K blocks.
     DmTable snap_table;
@@ -302,7 +307,6 @@
     snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
     ASSERT_TRUE(snapshot_dev_->valid());
     ASSERT_FALSE(snapshot_dev_->path().empty());
-    ASSERT_TRUE(snapshot_dev_->WaitForUdev());
 
     setup_ok_ = true;
 }
@@ -452,6 +456,87 @@
     }
 }
 
+TEST(libdm, ParseStatusText) {
+    DmTargetSnapshot::Status status;
+
+    // Bad inputs
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("X", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123/456", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456 789", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 456/789", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123/456/789", &status));
+    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText("123 / 456 789", &status));
+
+    // Good input
+    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("123/456 789", &status));
+    EXPECT_EQ(status.sectors_allocated, 123);
+    EXPECT_EQ(status.total_sectors, 456);
+    EXPECT_EQ(status.metadata_sectors, 789);
+
+    // Known error codes
+    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Invalid", &status));
+    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Merge failed", &status));
+    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Overflow", &status));
+}
+
+TEST(libdm, DmSnapshotMergePercent) {
+    DmTargetSnapshot::Status status;
+
+    // Correct input
+    status.sectors_allocated = 1000;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 1.0);
+
+    status.sectors_allocated = 500;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 49.0);
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 51.0);
+
+    status.sectors_allocated = 0;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);
+
+    status.sectors_allocated = 500;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 500;
+    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);
+
+    status.sectors_allocated = 500;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 500), 1.0);
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 1000), 51.0);
+    EXPECT_GE(DmTargetSnapshot::MergePercent(status, 1000), 49.0);
+
+    // Robustness
+    status.sectors_allocated = 2000;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 0;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+    status.sectors_allocated = 2000;
+    status.total_sectors = 1000;
+    status.metadata_sectors = 2000;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+    status.sectors_allocated = 2000;
+    status.total_sectors = 0;
+    status.metadata_sectors = 2000;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);
+
+    status.sectors_allocated = 1000;
+    status.total_sectors = 0;
+    status.metadata_sectors = 1000;
+    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 0), 0.0);
+}
+
 TEST(libdm, CryptArgs) {
     DmTargetCrypt target1(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
     ASSERT_EQ(target1.name(), "crypt");
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index afcb090..e25ce7f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -25,7 +25,9 @@
 #include <sys/sysmacros.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -38,13 +40,15 @@
 #define DM_VERSION2 (0)
 
 #define DM_ALIGN_MASK (7)
-#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
+#define DM_ALIGN(x) (((x) + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)
 
 namespace android {
 namespace dm {
 
 enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };
 
+static constexpr uint64_t kSectorSize = 512;
+
 class DeviceMapper final {
   public:
     class DmBlockDevice final {
@@ -69,21 +73,67 @@
         uint64_t dev_;
     };
 
+    class Info {
+        uint32_t flags_;
+
+      public:
+        explicit Info(uint32_t flags) : flags_(flags) {}
+
+        bool IsActiveTablePresent() const { return flags_ & DM_ACTIVE_PRESENT_FLAG; }
+        bool IsBufferFull() const { return flags_ & DM_BUFFER_FULL_FLAG; }
+        bool IsInactiveTablePresent() const { return flags_ & DM_INACTIVE_PRESENT_FLAG; }
+        bool IsReadOnly() const { return flags_ & DM_READONLY_FLAG; }
+        bool IsSuspended() const { return flags_ & DM_SUSPEND_FLAG; }
+    };
+
     // Removes a device mapper device with the given name.
     // Returns 'true' on success, false otherwise.
     bool DeleteDevice(const std::string& name);
+    bool DeleteDeviceIfExists(const std::string& name);
 
-    // Reads the device mapper table from the device with given anme and
-    // returns it in a DmTable object.
-    const std::unique_ptr<DmTable> table(const std::string& name) const;
+    // Fetches and returns the complete state of the underlying device mapper
+    // device with given name.
+    std::optional<Info> GetDetailedInfo(const std::string& name) const;
 
     // Returns the current state of the underlying device mapper device
     // with given name.
     // One of INVALID, SUSPENDED or ACTIVE.
     DmDeviceState GetState(const std::string& name) const;
 
+    // Puts the given device to the specified status, which must be either:
+    // - SUSPENDED: suspend the device, or
+    // - ACTIVE: resumes the device.
+    bool ChangeState(const std::string& name, DmDeviceState state);
+
     // Creates a device, loads the given table, and activates it. If the device
     // is not able to be activated, it is destroyed, and false is returned.
+    // After creation, |path| contains the result of calling
+    // GetDmDevicePathByName, and the path is guaranteed to exist. If after
+    // |timeout_ms| the path is not available, the device will be deleted and
+    // this function will return false.
+    //
+    // This variant must be used when depending on the device path. The
+    // following manual sequence should not be used:
+    //
+    //   1. CreateDevice(name, table)
+    //   2. GetDmDevicePathByName(name, &path)
+    //   3. fs_mgr::WaitForFile(path, <timeout>)
+    //
+    // This sequence has a race condition where, if another process deletes a
+    // device, CreateDevice may acquire the same path. When this happens, the
+    // WaitForFile() may early-return since ueventd has not yet processed all
+    // of the outstanding udev events. The caller may unexpectedly get an
+    // ENOENT on a system call using the affected path.
+    //
+    // If |timeout_ms| is 0ms, then this function will return true whether or
+    // not |path| is available. It is the caller's responsibility to ensure
+    // there are no races.
+    bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+                      const std::chrono::milliseconds& timeout_ms);
+
+    // Create a device and activate the given table, without waiting to acquire
+    // a valid path. If the caller will use GetDmDevicePathByName(), it should
+    // use the timeout variant above.
     bool CreateDevice(const std::string& name, const DmTable& table);
 
     // Loads the device mapper table from parameter into the underlying device
@@ -110,8 +160,21 @@
     // Returns the path to the device mapper device node in '/dev' corresponding to
     // 'name'. If the device does not exist, false is returned, and the path
     // parameter is not set.
+    //
+    // This returns a path in the format "/dev/block/dm-N" that can be easily
+    // re-used with sysfs.
+    //
+    // WaitForFile() should not be used in conjunction with this call, since it
+    // could race with ueventd.
     bool GetDmDevicePathByName(const std::string& name, std::string* path);
 
+    // Returns a device's unique path as generated by ueventd. This will return
+    // true as long as the device has been created, even if ueventd has not
+    // processed it yet.
+    //
+    // The formatting of this path is /dev/block/mapper/by-uuid/<uuid>.
+    bool GetDeviceUniquePath(const std::string& name, std::string* path);
+
     // Returns the dev_t for the named device-mapper node.
     bool GetDeviceNumber(const std::string& name, dev_t* dev);
 
@@ -135,6 +198,7 @@
     struct TargetInfo {
         struct dm_target_spec spec;
         std::string data;
+        TargetInfo() {}
         TargetInfo(const struct dm_target_spec& spec, const std::string& data)
             : spec(spec), data(data) {}
     };
@@ -158,18 +222,12 @@
     // limit we are imposing here of 256.
     static constexpr uint32_t kMaxPossibleDmDevices = 256;
 
+    bool CreateDevice(const std::string& name, const std::string& uuid = {});
     bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);
-
     void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
 
     DeviceMapper();
 
-    // Creates a device mapper device with given name.
-    // Return 'true' on success and 'false' on failure to
-    // create OR if a device mapper device with the same name already
-    // exists.
-    bool CreateDevice(const std::string& name);
-
     int fd_;
     // Non-copyable & Non-movable
     DeviceMapper(const DeviceMapper&) = delete;
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index e8885c7..a78bc71 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -216,8 +216,11 @@
         std::string error;
     };
 
+    static double MergePercent(const Status& status, uint64_t sectors_initial = 0);
     static bool ParseStatusText(const std::string& text, Status* status);
     static bool ReportsOverflow(const std::string& target_type);
+    static bool GetDevicesFromParams(const std::string& params, std::string* base_device,
+                                     std::string* cow_device);
 
   private:
     std::string base_device_;
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
index 6b4c2d8..eeed6b5 100644
--- a/fs_mgr/libdm/include/libdm/loop_control.h
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -17,6 +17,7 @@
 #ifndef _LIBDM_LOOP_CONTROL_H_
 #define _LIBDM_LOOP_CONTROL_H_
 
+#include <chrono>
 #include <string>
 
 #include <android-base/unique_fd.h>
@@ -29,8 +30,15 @@
     LoopControl();
 
     // Attaches the file specified by 'file_fd' to the loop device specified
-    // by 'loopdev'
-    bool Attach(int file_fd, std::string* loopdev) const;
+    // by 'loopdev'. It is possible that in between allocating and attaching
+    // a loop device, another process attaches to the chosen loop device. If
+    // this happens, Attach() will retry for up to |timeout_ms|. The timeout
+    // should not be zero.
+    //
+    // The caller does not have to call WaitForFile(); it is implicitly called.
+    // The given |timeout_ms| covers both potential sources of timeout.
+    bool Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,
+                std::string* loopdev) const;
 
     // Detach the loop device given by 'loopdev' from the attached backing file.
     bool Detach(const std::string& loopdev) const;
@@ -56,13 +64,13 @@
   public:
     // Create a loop device for the given file descriptor. It is closed when
     // LoopDevice is destroyed only if auto_close is true.
-    LoopDevice(int fd, bool auto_close = false);
+    LoopDevice(int 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.
-    explicit LoopDevice(const std::string& path);
+    LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms);
     ~LoopDevice();
 
-    bool valid() const { return fd_ != -1 && !device_.empty(); }
+    bool valid() const { return valid_; }
     const std::string& device() const { return device_; }
 
     LoopDevice(const LoopDevice&) = delete;
@@ -71,12 +79,13 @@
     LoopDevice(LoopDevice&&) = default;
 
   private:
-    void Init();
+    void Init(const std::chrono::milliseconds& timeout_ms);
 
     android::base::unique_fd fd_;
     bool owns_fd_;
     std::string device_;
     LoopControl control_;
+    bool valid_ = false;
 };
 
 }  // namespace dm
diff --git a/fs_mgr/libdm/loop_control.cpp b/fs_mgr/libdm/loop_control.cpp
index 16bf4b0..edc9a45 100644
--- a/fs_mgr/libdm/loop_control.cpp
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -27,6 +27,8 @@
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 
+#include "utility.h"
+
 namespace android {
 namespace dm {
 
@@ -37,21 +39,40 @@
     }
 }
 
-bool LoopControl::Attach(int file_fd, std::string* loopdev) const {
-    if (!FindFreeLoopDevice(loopdev)) {
-        LOG(ERROR) << "Failed to attach, no free loop devices";
-        return false;
-    }
+bool LoopControl::Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,
+                         std::string* loopdev) const {
+    auto start_time = std::chrono::steady_clock::now();
+    auto condition = [&]() -> WaitResult {
+        if (!FindFreeLoopDevice(loopdev)) {
+            LOG(ERROR) << "Failed to attach, no free loop devices";
+            return WaitResult::Fail;
+        }
 
-    android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
-    if (loop_fd < 0) {
-        PLOG(ERROR) << "Failed to open: " << *loopdev;
-        return false;
-    }
+        auto now = std::chrono::steady_clock::now();
+        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+        if (!WaitForFile(*loopdev, timeout_ms - time_elapsed)) {
+            LOG(ERROR) << "Timed out waiting for path: " << *loopdev;
+            return WaitResult::Fail;
+        }
 
-    int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd);
-    if (rc < 0) {
-        PLOG(ERROR) << "Failed LOOP_SET_FD";
+        android::base::unique_fd loop_fd(
+                TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));
+        if (loop_fd < 0) {
+            PLOG(ERROR) << "Failed to open: " << *loopdev;
+            return WaitResult::Fail;
+        }
+
+        if (int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd); rc == 0) {
+            return WaitResult::Done;
+        }
+        if (errno != EBUSY) {
+            PLOG(ERROR) << "Failed LOOP_SET_FD";
+            return WaitResult::Fail;
+        }
+        return WaitResult::Wait;
+    };
+    if (!WaitForCondition(condition, timeout_ms)) {
+        LOG(ERROR) << "Timed out trying to acquire a loop device";
         return false;
     }
     return true;
@@ -112,17 +133,19 @@
     return true;
 }
 
-LoopDevice::LoopDevice(int fd, bool auto_close) : fd_(fd), owns_fd_(auto_close) {
-    Init();
+LoopDevice::LoopDevice(int fd, const std::chrono::milliseconds& timeout_ms, bool auto_close)
+    : fd_(fd), owns_fd_(auto_close) {
+    Init(timeout_ms);
 }
 
-LoopDevice::LoopDevice(const std::string& path) : fd_(-1), owns_fd_(true) {
+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) {
         PLOG(ERROR) << "open failed for " << path;
         return;
     }
-    Init();
+    Init(timeout_ms);
 }
 
 LoopDevice::~LoopDevice() {
@@ -134,8 +157,8 @@
     }
 }
 
-void LoopDevice::Init() {
-    control_.Attach(fd_, &device_);
+void LoopDevice::Init(const std::chrono::milliseconds& timeout_ms) {
+    valid_ = control_.Attach(fd_, timeout_ms, &device_);
 }
 
 }  // namespace dm
diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp
index 08bdc00..0749f26 100644
--- a/fs_mgr/libdm/loop_control_test.cpp
+++ b/fs_mgr/libdm/loop_control_test.cpp
@@ -53,7 +53,7 @@
     unique_fd fd = TempFile();
     ASSERT_GE(fd, 0);
 
-    LoopDevice loop(fd);
+    LoopDevice loop(fd, 10s);
     ASSERT_TRUE(loop.valid());
 
     char buffer[6];
diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp
new file mode 100644
index 0000000..eccf2fb
--- /dev/null
+++ b/fs_mgr/libdm/utility.cpp
@@ -0,0 +1,56 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "utility.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <thread>
+
+using namespace std::literals;
+
+namespace android {
+namespace dm {
+
+bool WaitForCondition(const std::function<WaitResult()>& condition,
+                      const std::chrono::milliseconds& timeout_ms) {
+    auto start_time = std::chrono::steady_clock::now();
+    while (true) {
+        auto result = condition();
+        if (result == WaitResult::Done) return true;
+        if (result == WaitResult::Fail) return false;
+
+        std::this_thread::sleep_for(20ms);
+
+        auto now = std::chrono::steady_clock::now();
+        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+        if (time_elapsed > timeout_ms) return false;
+    }
+}
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms) {
+    auto condition = [&]() -> WaitResult {
+        // If the file exists but returns EPERM or something, we consider the
+        // condition met.
+        if (access(path.c_str(), F_OK) != 0) {
+            if (errno == ENOENT) return WaitResult::Wait;
+        }
+        return WaitResult::Done;
+    };
+    return WaitForCondition(condition, timeout_ms);
+}
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libdm/utility.h b/fs_mgr/libdm/utility.h
new file mode 100644
index 0000000..f1dce9e
--- /dev/null
+++ b/fs_mgr/libdm/utility.h
@@ -0,0 +1,30 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <chrono>
+#include <functional>
+
+namespace android {
+namespace dm {
+
+enum class WaitResult { Wait, Done, Fail };
+
+bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms);
+bool WaitForCondition(const std::function<WaitResult()>& condition,
+                      const std::chrono::milliseconds& timeout_ms);
+
+}  // namespace dm
+}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/.clang-format b/fs_mgr/libfiemap_writer/.clang-format
deleted file mode 120000
index 8b770a1..0000000
--- a/fs_mgr/libfiemap_writer/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../../.clang-format-4
\ No newline at end of file
diff --git a/fs_mgr/libfiemap_writer/Android.bp b/fs_mgr/libfiemap_writer/Android.bp
deleted file mode 100644
index ed209aa..0000000
--- a/fs_mgr/libfiemap_writer/Android.bp
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_library_static {
-    name: "libfiemap_writer",
-    defaults: ["fs_mgr_defaults"],
-    recovery_available: true,
-    export_include_dirs: ["include"],
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-    ],
-
-    srcs: [
-        "fiemap_writer.cpp",
-        "split_fiemap_writer.cpp",
-        "utility.cpp",
-    ],
-
-    static_libs: [
-        "libdm",
-        "libext4_utils",
-    ],
-
-    header_libs: [
-        "libbase_headers",
-        "liblog_headers",
-    ],
-}
-
-cc_test {
-    name: "fiemap_writer_test",
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-    ],
-    static_libs: [
-        "libbase",
-        "libdm",
-        "libfiemap_writer",
-        "liblog",
-    ],
-
-    data: [
-        "testdata/unaligned_file",
-        "testdata/file_4k",
-        "testdata/file_32k",
-    ],
-
-    srcs: [
-        "fiemap_writer_test.cpp",
-    ],
-}
diff --git a/fs_mgr/libfiemap_writer/Android.mk b/fs_mgr/libfiemap_writer/Android.mk
deleted file mode 100644
index 3c07b8e..0000000
--- a/fs_mgr/libfiemap_writer/Android.mk
+++ /dev/null
@@ -1,22 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsFiemapWriterTest
--include test/vts/tools/build/Android.host_config.mk
diff --git a/fs_mgr/libfiemap_writer/AndroidTest.xml b/fs_mgr/libfiemap_writer/AndroidTest.xml
deleted file mode 100644
index 08cff0e..0000000
--- a/fs_mgr/libfiemap_writer/AndroidTest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Config for VTS VtsFiemapWriterTest">
-    <option name="config-descriptor:metadata" key="plan" value="vts-kernel" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="abort-on-push-failure" value="false"/>
-        <option name="push-group" value="HostDrivenTest.push"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-      <option name="test-module-name" value="VtsFiemapWriterTest"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/fiemap_writer_test/fiemap_writer_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/fiemap_writer_test/fiemap_writer_test" />
-        <option name="binary-test-type" value="gtest"/>
-        <option name="test-timeout" value="1m"/>
-    </test>
-</configuration>
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
deleted file mode 100644
index 0a3ba6c..0000000
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ /dev/null
@@ -1,745 +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 <libfiemap_writer/fiemap_writer.h>
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <stdio.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <limits>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm.h>
-
-namespace android {
-namespace fiemap_writer {
-
-using namespace android::dm;
-
-// We are expecting no more than 512 extents in a fiemap of the file we create.
-// If we find more, then it is treated as error for now.
-static constexpr const uint32_t kMaxExtents = 512;
-
-// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
-static constexpr const uint32_t kUnsupportedExtentFlags =
-        FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
-        FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
-        FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
-
-// Large file support must be enabled.
-static_assert(sizeof(off_t) == sizeof(uint64_t));
-
-static inline void cleanup(const std::string& file_path, bool created) {
-    if (created) {
-        unlink(file_path.c_str());
-    }
-}
-
-static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
-    // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
-    // The directory name in the target corresponds to the name of the block device. We use
-    // that to extract the block device name.
-    // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
-    // follows.
-    //    1:0 -> ../../devices/virtual/block/ram0
-    std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
-    std::string sysfs_bdev;
-
-    if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
-        PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
-        return false;
-    }
-
-    *bdev_name = ::android::base::Basename(sysfs_bdev);
-    // Paranoid sanity check to make sure we just didn't get the
-    // input in return as-is.
-    if (sysfs_bdev == *bdev_name) {
-        LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
-        return false;
-    }
-
-    return true;
-}
-
-static bool ValidateDmTarget(const DeviceMapper::TargetInfo& target) {
-    const auto& entry = target.spec;
-    if (entry.sector_start != 0) {
-        LOG(INFO) << "Stopping at target with non-zero starting sector";
-        return false;
-    }
-
-    auto target_type = DeviceMapper::GetTargetType(entry);
-    if (target_type == "bow" || target_type == "default-key" || target_type == "crypt") {
-        return true;
-    }
-    if (target_type == "linear") {
-        auto pieces = android::base::Split(target.data, " ");
-        if (pieces[1] != "0") {
-            LOG(INFO) << "Stopping at complex linear target with non-zero starting sector: "
-                      << pieces[1];
-            return false;
-        }
-        return true;
-    }
-
-    LOG(INFO) << "Stopping at complex target type " << target_type;
-    return false;
-}
-
-static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
-    *bdev_raw = bdev;
-
-    if (!::android::base::StartsWith(bdev, "dm-")) {
-        // We are at the bottom of the device mapper stack.
-        return true;
-    }
-
-    // Get the device name.
-    auto dm_name_file = "/sys/block/" + bdev + "/dm/name";
-    std::string dm_name;
-    if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
-        PLOG(ERROR) << "Could not read file: " << dm_name_file;
-        return false;
-    }
-    dm_name = android::base::Trim(dm_name);
-
-    auto& dm = DeviceMapper::Instance();
-    std::vector<DeviceMapper::TargetInfo> table;
-    if (!dm.GetTableInfo(dm_name, &table)) {
-        LOG(ERROR) << "Could not read device-mapper table for " << dm_name << " at " << bdev;
-        return false;
-    }
-
-    // The purpose of libfiemap_writer is to provide an extent-based view into
-    // a file. This is difficult if devices are not layered in a 1:1 manner;
-    // we would have to translate and break up extents based on the actual
-    // block mapping. Since this is too complex, we simply stop processing
-    // the device-mapper stack if we encounter a complex case.
-    //
-    // It is up to the caller to decide whether stopping at a virtual block
-    // device is allowable. In most cases it is not, because we want either
-    // "userdata" or an external volume. It is useful for tests however.
-    // Callers can check by comparing the device number to that of userdata,
-    // or by checking whether is a device-mapper node.
-    if (table.size() > 1) {
-        LOG(INFO) << "Stopping at complex table for " << dm_name << " at " << bdev;
-        return true;
-    }
-    if (!ValidateDmTarget(table[0])) {
-        return true;
-    }
-
-    auto dm_leaf_dir = "/sys/block/" + bdev + "/slaves";
-    auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
-    if (d == nullptr) {
-        PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
-        return false;
-    }
-
-    struct dirent* de;
-    uint32_t num_leaves = 0;
-    std::string bdev_next = "";
-    while ((de = readdir(d.get())) != nullptr) {
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
-            continue;
-        }
-
-        // We set the first name we find here
-        if (bdev_next.empty()) {
-            bdev_next = de->d_name;
-        }
-        num_leaves++;
-    }
-
-    // if we have more than one leaves, we return immediately. We can't continue to create the
-    // file since we don't know how to write it out using fiemap, so it will be readable via the
-    // underlying block devices later. The reader will also have to construct the same device mapper
-    // target in order read the file out.
-    if (num_leaves > 1) {
-        LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
-                   << bdev;
-        return false;
-    }
-
-    // recursively call with the block device we found in order to pop the device mapper stack.
-    return DeviceMapperStackPop(bdev_next, bdev_raw);
-}
-
-bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
-                                         bool* uses_dm) {
-    struct stat sb;
-    if (stat(file_path.c_str(), &sb)) {
-        PLOG(ERROR) << "Failed to get stat for: " << file_path;
-        return false;
-    }
-
-    std::string bdev;
-    if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
-        LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
-                   << minor(sb.st_dev);
-        return false;
-    }
-
-    std::string bdev_raw;
-    if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
-        LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
-        return false;
-    }
-
-    if (uses_dm) {
-        *uses_dm = (bdev_raw != bdev);
-    }
-
-    LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
-               << bdev << ")";
-
-    *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
-
-    // Make sure we are talking to a block device before calling it a success.
-    if (stat(bdev_path->c_str(), &sb)) {
-        PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
-        return false;
-    }
-
-    if ((sb.st_mode & S_IFMT) != S_IFBLK) {
-        PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
-        return false;
-    }
-
-    return true;
-}
-
-static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
-    uint64_t size_in_bytes = 0;
-    if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
-        PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
-        return false;
-    }
-
-    *bdev_size = size_in_bytes;
-
-    return true;
-}
-
-static uint64_t GetFileSize(const std::string& file_path) {
-    struct stat sb;
-    if (stat(file_path.c_str(), &sb)) {
-        PLOG(ERROR) << "Failed to get size for file: " << file_path;
-        return 0;
-    }
-
-    return sb.st_size;
-}
-
-static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
-                              uint32_t* fs_type) {
-    struct statfs64 sfs;
-    if (statfs64(file_path.c_str(), &sfs)) {
-        PLOG(ERROR) << "Failed to read file system status at: " << file_path;
-        return false;
-    }
-
-    if (!sfs.f_bsize) {
-        LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
-        return false;
-    }
-
-    // Check if the filesystem is of supported types.
-    // Only ext4, f2fs, and vfat are tested and supported.
-    switch (sfs.f_type) {
-        case EXT4_SUPER_MAGIC:
-        case F2FS_SUPER_MAGIC:
-        case MSDOS_SUPER_MAGIC:
-            break;
-        default:
-            LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
-            return false;
-    }
-
-    uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
-    if (available_bytes <= file_size) {
-        LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
-        return false;
-    }
-
-    *blocksz = sfs.f_bsize;
-    *fs_type = sfs.f_type;
-    return true;
-}
-
-static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
-                              const std::string& file_path,
-                              const std::function<bool(uint64_t, uint64_t)>& on_progress) {
-    // Even though this is much faster than writing zeroes, it is still slow
-    // enough that we need to fire the progress callback periodically. To
-    // easily achieve this, we seek in chunks. We use 1000 chunks since
-    // normally we only fire the callback on 1/1000th increments.
-    uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
-
-    // Seek just to the end of each chunk and write a single byte, causing
-    // the filesystem to allocate blocks.
-    off_t cursor = 0;
-    off_t end = static_cast<off_t>(file_size);
-    while (cursor < end) {
-        cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
-        auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
-        if (rv < 0) {
-            PLOG(ERROR) << "Failed to lseek " << file_path;
-            return false;
-        }
-        if (rv != cursor - 1) {
-            LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
-            return false;
-        }
-        char buffer[] = {0};
-        if (!android::base::WriteFully(file_fd, buffer, 1)) {
-            PLOG(ERROR) << "Write failed: " << file_path;
-            return false;
-        }
-        if (on_progress && !on_progress(cursor, file_size)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
-                         uint64_t file_size, unsigned int fs_type,
-                         std::function<bool(uint64_t, uint64_t)> on_progress) {
-    // Reserve space for the file on the file system and write it out to make sure the extents
-    // don't come back unwritten. Return from this function with the kernel file offset set to 0.
-    // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
-    // aren't moved around.
-    switch (fs_type) {
-        case EXT4_SUPER_MAGIC:
-        case F2FS_SUPER_MAGIC:
-            if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
-                PLOG(ERROR) << "Failed to allocate space for file: " << file_path
-                            << " size: " << file_size;
-                return false;
-            }
-            break;
-        case MSDOS_SUPER_MAGIC:
-            // fallocate() is not supported, and not needed, since VFAT does not support holes.
-            // Instead we can perform a much faster allocation.
-            return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
-        default:
-            LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
-            return false;
-    }
-
-    // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
-    // blocks are actually written to by the file system and thus getting rid of the holes in the
-    // file.
-    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
-    if (buffer == nullptr) {
-        LOG(ERROR) << "failed to allocate memory for writing file";
-        return false;
-    }
-
-    off64_t offset = lseek64(file_fd, 0, SEEK_SET);
-    if (offset < 0) {
-        PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
-        return false;
-    }
-
-    int permille = -1;
-    while (offset < file_size) {
-        if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
-            PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
-                        << " in file " << file_path;
-            return false;
-        }
-
-        offset += blocksz;
-
-        // Don't invoke the callback every iteration - wait until a significant
-        // chunk (here, 1/1000th) of the data has been processed.
-        int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
-        if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
-            if (on_progress && !on_progress(offset, file_size)) {
-                return false;
-            }
-            permille = new_permille;
-        }
-    }
-
-    if (lseek64(file_fd, 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
-        return false;
-    }
-
-    // flush all writes here ..
-    if (fsync(file_fd)) {
-        PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
-        return false;
-    }
-
-    // Send one last progress notification.
-    if (on_progress && !on_progress(file_size, file_size)) {
-        return false;
-    }
-    return true;
-}
-
-static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
-    if (fs_type != F2FS_SUPER_MAGIC) {
-        // No pinning necessary for ext4/msdos. The blocks, once allocated, are
-        // expected to be fixed.
-        return true;
-    }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_SET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
-#endif
-
-    uint32_t pin_status = 1;
-    int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
-    if (error < 0) {
-        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
-            PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
-        } else {
-            PLOG(ERROR) << "Failed to pin file: " << file_path;
-        }
-        return false;
-    }
-
-    return true;
-}
-
-static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
-    if (fs_type != F2FS_SUPER_MAGIC) {
-        // No pinning necessary for ext4 or vfat. The blocks, once allocated,
-        // are expected to be fixed.
-        return true;
-    }
-
-// F2FS-specific ioctl
-// It requires the below kernel commit merged in v4.16-rc1.
-//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
-// In android-4.4,
-//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
-// In android-4.9,
-//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
-// In android-4.14,
-//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
-#ifndef F2FS_IOC_GET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
-#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
-#endif
-
-    // f2fs: export FS_NOCOW_FL flag to user
-    uint32_t flags;
-    int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
-    if (error < 0) {
-        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
-            PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
-        } else {
-            PLOG(ERROR) << "Failed to get flags: " << file_path;
-        }
-        return false;
-    }
-    if (!(flags & FS_NOCOW_FL)) {
-        LOG(ERROR) << "It is not pinned: " << file_path;
-        return false;
-    }
-
-    // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
-    uint32_t moved_blocks_nr;
-    error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
-    if (error < 0) {
-        if ((errno == ENOTTY) || (errno == ENOTSUP)) {
-            PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
-        } else {
-            PLOG(ERROR) << "Failed to get file pin status: " << file_path;
-        }
-        return false;
-    }
-
-    if (moved_blocks_nr) {
-        LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
-    }
-    return moved_blocks_nr == 0;
-}
-
-bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
-    android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));
-    if (fd < 0) {
-        PLOG(ERROR) << "open: " << file_path;
-        return false;
-    }
-
-    struct statfs64 sfs;
-    if (fstatfs64(fd, &sfs)) {
-        PLOG(ERROR) << "fstatfs64: " << file_path;
-        return false;
-    }
-    return IsFilePinned(fd, file_path, sfs.f_type);
-}
-
-static bool ReadFiemap(int file_fd, const std::string& file_path,
-                       std::vector<struct fiemap_extent>* extents) {
-    uint64_t fiemap_size =
-            sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
-    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
-    if (buffer == nullptr) {
-        LOG(ERROR) << "Failed to allocate memory for fiemap";
-        return false;
-    }
-
-    struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
-    fiemap->fm_start = 0;
-    fiemap->fm_length = UINT64_MAX;
-    // make sure file is synced to disk before we read the fiemap
-    fiemap->fm_flags = FIEMAP_FLAG_SYNC;
-    fiemap->fm_extent_count = kMaxExtents;
-
-    if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
-        PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
-        return false;
-    }
-
-    if (fiemap->fm_mapped_extents == 0) {
-        LOG(ERROR) << "File " << file_path << " has zero extents";
-        return false;
-    }
-
-    // Iterate through each extent read and make sure its valid before adding it to the vector
-    bool last_extent_seen = false;
-    struct fiemap_extent* extent = &fiemap->fm_extents[0];
-    for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
-        // LogExtent(i + 1, *extent);
-        if (extent->fe_flags & kUnsupportedExtentFlags) {
-            LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
-                       << " has unsupported flags";
-            extents->clear();
-            return false;
-        }
-
-        if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
-            last_extent_seen = true;
-            if (i != (fiemap->fm_mapped_extents - 1)) {
-                LOG(WARNING) << "Extents are being received out-of-order";
-            }
-        }
-        extents->emplace_back(std::move(*extent));
-    }
-
-    if (!last_extent_seen) {
-        // The file is possibly too fragmented.
-        if (fiemap->fm_mapped_extents == kMaxExtents) {
-            LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
-        }
-        extents->clear();
-    }
-
-    return last_extent_seen;
-}
-
-static bool ReadFibmap(int file_fd, const std::string& file_path,
-                       std::vector<struct fiemap_extent>* extents) {
-    struct stat s;
-    if (fstat(file_fd, &s)) {
-        PLOG(ERROR) << "Failed to stat " << file_path;
-        return false;
-    }
-
-    unsigned int blksize;
-    if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
-        PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
-        return false;
-    }
-    if (!blksize) {
-        LOG(ERROR) << "Invalid filesystem block size: " << blksize;
-        return false;
-    }
-
-    uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
-    if (num_blocks > std::numeric_limits<uint32_t>::max()) {
-        LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
-        return false;
-    }
-
-    for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
-        uint32_t block = block_number;
-        if (ioctl(file_fd, FIBMAP, &block)) {
-            PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
-            return false;
-        }
-        if (!block) {
-            LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
-            return false;
-        }
-
-        if (!extents->empty() && block == last_block + 1) {
-            extents->back().fe_length += blksize;
-        } else {
-            extents->push_back(fiemap_extent{.fe_logical = block_number,
-                                             .fe_physical = static_cast<uint64_t>(block) * blksize,
-                                             .fe_length = static_cast<uint64_t>(blksize),
-                                             .fe_flags = 0});
-        }
-        last_block = block;
-    }
-    return true;
-}
-
-FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
-                                   std::function<bool(uint64_t, uint64_t)> progress) {
-    // if 'create' is false, open an existing file and do not truncate.
-    int open_flags = O_RDWR | O_CLOEXEC;
-    if (create) {
-        if (access(file_path.c_str(), F_OK) == 0) {
-            LOG(WARNING) << "File " << file_path << " already exists, truncating";
-        }
-        open_flags |= O_CREAT | O_TRUNC;
-    }
-    ::android::base::unique_fd file_fd(
-            TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
-    if (file_fd < 0) {
-        PLOG(ERROR) << "Failed to create file at: " << file_path;
-        return nullptr;
-    }
-
-    std::string abs_path;
-    if (!::android::base::Realpath(file_path, &abs_path)) {
-        PLOG(ERROR) << "Invalid file path: " << file_path;
-        cleanup(file_path, create);
-        return nullptr;
-    }
-
-    std::string bdev_path;
-    if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
-        LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
-        cleanup(abs_path, create);
-        return nullptr;
-    }
-
-    ::android::base::unique_fd bdev_fd(
-            TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (bdev_fd < 0) {
-        PLOG(ERROR) << "Failed to open block device: " << bdev_path;
-        cleanup(file_path, create);
-        return nullptr;
-    }
-
-    uint64_t bdevsz;
-    if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
-        LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
-        cleanup(file_path, create);
-        return nullptr;
-    }
-
-    if (!create) {
-        file_size = GetFileSize(abs_path);
-        if (file_size == 0) {
-            LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
-            return nullptr;
-        }
-    }
-
-    uint64_t blocksz;
-    uint32_t fs_type;
-    if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
-        LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
-        cleanup(abs_path, create);
-        return nullptr;
-    }
-
-    // Align up to the nearest block size.
-    if (file_size % blocksz) {
-        file_size += blocksz - (file_size % blocksz);
-    }
-
-    if (create) {
-        if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
-            LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
-                       << " bytes";
-            cleanup(abs_path, create);
-            return nullptr;
-        }
-    }
-
-    // f2fs may move the file blocks around.
-    if (!PinFile(file_fd, abs_path, fs_type)) {
-        cleanup(abs_path, create);
-        LOG(ERROR) << "Failed to pin the file in storage";
-        return nullptr;
-    }
-
-    // now allocate the FiemapWriter and start setting it up
-    FiemapUniquePtr fmap(new FiemapWriter());
-    switch (fs_type) {
-        case EXT4_SUPER_MAGIC:
-        case F2FS_SUPER_MAGIC:
-            if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
-                LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
-                cleanup(abs_path, create);
-                return nullptr;
-            }
-            break;
-        case MSDOS_SUPER_MAGIC:
-            if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
-                LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
-                cleanup(abs_path, create);
-                return nullptr;
-            }
-            break;
-    }
-
-    fmap->file_path_ = abs_path;
-    fmap->bdev_path_ = bdev_path;
-    fmap->file_size_ = file_size;
-    fmap->bdev_size_ = bdevsz;
-    fmap->fs_type_ = fs_type;
-    fmap->block_size_ = blocksz;
-
-    LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
-                 << bdev_path;
-    return fmap;
-}
-
-}  // namespace fiemap_writer
-}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp b/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
deleted file mode 100644
index dda7dfd..0000000
--- a/fs_mgr/libfiemap_writer/fiemap_writer_test.cpp
+++ /dev/null
@@ -1,541 +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 <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-#include <libdm/loop_control.h>
-#include <libfiemap_writer/fiemap_writer.h>
-#include <libfiemap_writer/split_fiemap_writer.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap_writer {
-
-using namespace std;
-using namespace std::string_literals;
-using namespace android::fiemap_writer;
-using unique_fd = android::base::unique_fd;
-using LoopDevice = android::dm::LoopDevice;
-
-std::string gTestDir;
-uint64_t testfile_size = 536870912;  // default of 512MiB
-size_t gBlockSize = 0;
-
-class FiemapWriterTest : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        testfile = gTestDir + "/"s + tinfo->name();
-    }
-
-    void TearDown() override { unlink(testfile.c_str()); }
-
-    // name of the file we use for testing
-    std::string testfile;
-};
-
-class SplitFiemapTest : public ::testing::Test {
-  protected:
-    void SetUp() override {
-        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
-        testfile = gTestDir + "/"s + tinfo->name();
-    }
-
-    void TearDown() override {
-        std::string message;
-        if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {
-            cerr << "Could not remove all split files: " << message;
-        }
-    }
-
-    // name of the file we use for testing
-    std::string testfile;
-};
-
-TEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {
-    // Try creating a file of size ~100TB but aligned to
-    // 512 byte to make sure block alignment tests don't
-    // fail.
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);
-    EXPECT_EQ(fptr, nullptr);
-    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
-    EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, CreateUnalignedFile) {
-    // Try creating a file of size 4097 bytes which is guaranteed
-    // to be unaligned to all known block sizes.
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);
-    ASSERT_NE(fptr, nullptr);
-    ASSERT_EQ(fptr->size(), gBlockSize * 2);
-}
-
-TEST_F(FiemapWriterTest, CheckFilePath) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
-    ASSERT_NE(fptr, nullptr);
-    EXPECT_EQ(fptr->size(), gBlockSize);
-    EXPECT_EQ(fptr->file_path(), testfile);
-    EXPECT_EQ(access(testfile.c_str(), F_OK), 0);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSize) {
-    // Create a large-ish file and test that the expected size matches.
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);
-    ASSERT_NE(fptr, nullptr);
-
-    struct stat s;
-    ASSERT_EQ(stat(testfile.c_str(), &s), 0);
-    EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());
-}
-
-TEST_F(FiemapWriterTest, CheckProgress) {
-    std::vector<uint64_t> expected;
-    size_t invocations = 0;
-    auto callback = [&](uint64_t done, uint64_t total) -> bool {
-        if (invocations >= expected.size()) {
-            return false;
-        }
-        EXPECT_EQ(done, expected[invocations]);
-        EXPECT_EQ(total, gBlockSize);
-        invocations++;
-        return true;
-    };
-
-    expected.push_back(gBlockSize);
-
-    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
-    EXPECT_NE(ptr, nullptr);
-    EXPECT_EQ(invocations, expected.size());
-}
-
-TEST_F(FiemapWriterTest, CheckPinning) {
-    auto ptr = FiemapWriter::Open(testfile, 4096);
-    ASSERT_NE(ptr, nullptr);
-    EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));
-}
-
-TEST_F(FiemapWriterTest, CheckBlockDevicePath) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
-    EXPECT_EQ(fptr->size(), gBlockSize);
-    EXPECT_EQ(fptr->bdev_path().find("/dev/block/"), size_t(0));
-    EXPECT_EQ(fptr->bdev_path().find("/dev/block/dm-"), string::npos);
-}
-
-TEST_F(FiemapWriterTest, CheckFileCreated) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);
-    ASSERT_NE(fptr, nullptr);
-    unique_fd fd(open(testfile.c_str(), O_RDONLY));
-    EXPECT_GT(fd, -1);
-}
-
-TEST_F(FiemapWriterTest, CheckFileSizeActual) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
-    ASSERT_NE(fptr, nullptr);
-
-    struct stat sb;
-    ASSERT_EQ(stat(testfile.c_str(), &sb), 0);
-    EXPECT_GE(sb.st_size, testfile_size);
-}
-
-TEST_F(FiemapWriterTest, CheckFileExtents) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);
-    ASSERT_NE(fptr, nullptr);
-    EXPECT_GT(fptr->extents().size(), 0);
-}
-
-TEST_F(FiemapWriterTest, ExistingFile) {
-    // Create the file.
-    { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }
-    // Test that we can still open it.
-    {
-        auto ptr = FiemapWriter::Open(testfile, 0, false);
-        ASSERT_NE(ptr, nullptr);
-        EXPECT_GT(ptr->extents().size(), 0);
-    }
-}
-
-TEST_F(FiemapWriterTest, FileDeletedOnError) {
-    auto callback = [](uint64_t, uint64_t) -> bool { return false; };
-    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));
-    EXPECT_EQ(ptr, nullptr);
-    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
-    EXPECT_EQ(errno, ENOENT);
-}
-
-TEST_F(FiemapWriterTest, MaxBlockSize) {
-    ASSERT_GT(DetermineMaximumFileSize(testfile), 0);
-}
-
-TEST_F(FiemapWriterTest, FibmapBlockAddressing) {
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);
-    ASSERT_NE(fptr, nullptr);
-
-    switch (fptr->fs_type()) {
-        case F2FS_SUPER_MAGIC:
-        case EXT4_SUPER_MAGIC:
-            // Skip the test for FIEMAP supported filesystems. This is really
-            // because f2fs/ext4 have caches that seem to defeat reading back
-            // directly from the block device, and writing directly is too
-            // dangerous.
-            std::cout << "Skipping test, filesystem does not use FIBMAP\n";
-            return;
-    }
-
-    bool uses_dm;
-    std::string bdev_path;
-    ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));
-
-    if (uses_dm) {
-        // We could use a device-mapper wrapper here to bypass encryption, but
-        // really this test is for FIBMAP correctness on VFAT (where encryption
-        // is never used), so we don't bother.
-        std::cout << "Skipping test, block device is metadata encrypted\n";
-        return;
-    }
-
-    std::string data(fptr->size(), '\0');
-    for (size_t i = 0; i < data.size(); i++) {
-        data[i] = 'A' + static_cast<char>(data.size() % 26);
-    }
-
-    {
-        unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));
-        ASSERT_GE(fd, 0);
-        ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
-        ASSERT_EQ(fsync(fd), 0);
-    }
-
-    ASSERT_FALSE(fptr->extents().empty());
-    const auto& first_extent = fptr->extents()[0];
-
-    unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));
-    ASSERT_GE(bdev, 0);
-
-    off_t where = first_extent.fe_physical;
-    ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);
-
-    // Note: this will fail on encrypted folders.
-    std::string actual(data.size(), '\0');
-    ASSERT_GE(first_extent.fe_length, data.size());
-    ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));
-    EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, Create) {
-    auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
-    ASSERT_NE(ptr, nullptr);
-
-    auto extents = ptr->extents();
-
-    // Destroy the fiemap, closing file handles. This should not delete them.
-    ptr = nullptr;
-
-    std::vector<std::string> files;
-    ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));
-    for (const auto& path : files) {
-        EXPECT_EQ(access(path.c_str(), F_OK), 0);
-    }
-
-    ASSERT_GE(extents.size(), files.size());
-}
-
-TEST_F(SplitFiemapTest, Open) {
-    {
-        auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
-        ASSERT_NE(ptr, nullptr);
-    }
-
-    auto ptr = SplitFiemap::Open(testfile);
-    ASSERT_NE(ptr, nullptr);
-
-    auto extents = ptr->extents();
-    ASSERT_GE(extents.size(), 24);
-}
-
-TEST_F(SplitFiemapTest, DeleteOnFail) {
-    auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);
-    ASSERT_EQ(ptr, nullptr);
-
-    std::string first_file = testfile + ".0001";
-    ASSERT_NE(access(first_file.c_str(), F_OK), 0);
-    ASSERT_EQ(errno, ENOENT);
-    ASSERT_NE(access(testfile.c_str(), F_OK), 0);
-    ASSERT_EQ(errno, ENOENT);
-}
-
-static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
-    std::string result;
-    for (int i = 0; i < num_files; i++) {
-        std::string path = base_path + android::base::StringPrintf(".%04d", i);
-        std::string data;
-        if (!android::base::ReadFileToString(path, &data)) {
-            return {};
-        }
-        result += data;
-    }
-    return result;
-}
-
-TEST_F(SplitFiemapTest, WriteWholeFile) {
-    static constexpr size_t kChunkSize = 32768;
-    static constexpr size_t kSize = kChunkSize * 3;
-    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
-    ASSERT_NE(ptr, nullptr);
-
-    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
-    for (size_t i = 0; i < kSize / sizeof(int); i++) {
-        buffer[i] = i;
-    }
-    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
-
-    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
-    auto actual = ReadSplitFiles(testfile, 3);
-    ASSERT_EQ(expected.size(), actual.size());
-    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks1) {
-    static constexpr size_t kChunkSize = 32768;
-    static constexpr size_t kSize = kChunkSize * 3;
-    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
-    ASSERT_NE(ptr, nullptr);
-
-    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
-    for (size_t i = 0; i < kSize / sizeof(int); i++) {
-        buffer[i] = i;
-    }
-
-    // Write in chunks of 1000 (so some writes straddle the boundary of two
-    // files).
-    size_t bytes_written = 0;
-    while (bytes_written < kSize) {
-        size_t to_write = std::min(kSize - bytes_written, (size_t)1000);
-        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
-        ASSERT_TRUE(ptr->Write(data, to_write));
-        bytes_written += to_write;
-    }
-
-    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
-    auto actual = ReadSplitFiles(testfile, 3);
-    ASSERT_EQ(expected.size(), actual.size());
-    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WriteFileInChunks2) {
-    static constexpr size_t kChunkSize = 32768;
-    static constexpr size_t kSize = kChunkSize * 3;
-    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
-    ASSERT_NE(ptr, nullptr);
-
-    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
-    for (size_t i = 0; i < kSize / sizeof(int); i++) {
-        buffer[i] = i;
-    }
-
-    // Write in chunks of 32KiB so every write is exactly at the end of the
-    // current file.
-    size_t bytes_written = 0;
-    while (bytes_written < kSize) {
-        size_t to_write = std::min(kSize - bytes_written, kChunkSize);
-        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;
-        ASSERT_TRUE(ptr->Write(data, to_write));
-        bytes_written += to_write;
-    }
-
-    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);
-    auto actual = ReadSplitFiles(testfile, 3);
-    ASSERT_EQ(expected.size(), actual.size());
-    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);
-}
-
-TEST_F(SplitFiemapTest, WritePastEnd) {
-    static constexpr size_t kChunkSize = 32768;
-    static constexpr size_t kSize = kChunkSize * 3;
-    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);
-    ASSERT_NE(ptr, nullptr);
-
-    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));
-    for (size_t i = 0; i < kSize / sizeof(int); i++) {
-        buffer[i] = i;
-    }
-    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));
-    ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
-}
-
-class VerifyBlockWritesExt4 : public ::testing::Test {
-    // 2GB Filesystem and 4k block size by default
-    static constexpr uint64_t block_size = 4096;
-    static constexpr uint64_t fs_size = 2147483648;
-
-  protected:
-    void SetUp() override {
-        fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
-        uint64_t count = fs_size / block_size;
-        std::string dd_cmd =
-                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
-                                              " count=%" PRIu64 " > /dev/null 2>&1",
-                                              fs_path.c_str(), block_size, count);
-        std::string mkfs_cmd =
-                ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
-        // create mount point
-        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
-        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
-        // create file for the file system
-        int ret = system(dd_cmd.c_str());
-        ASSERT_EQ(ret, 0);
-        // Get and attach a loop device to the filesystem we created
-        LoopDevice loop_dev(fs_path);
-        ASSERT_TRUE(loop_dev.valid());
-        // create file system
-        ret = system(mkfs_cmd.c_str());
-        ASSERT_EQ(ret, 0);
-
-        // mount the file system
-        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
-    }
-
-    void TearDown() override {
-        umount(mntpoint.c_str());
-        rmdir(mntpoint.c_str());
-        unlink(fs_path.c_str());
-    }
-
-    std::string mntpoint;
-    std::string fs_path;
-};
-
-class VerifyBlockWritesF2fs : public ::testing::Test {
-    // 2GB Filesystem and 4k block size by default
-    static constexpr uint64_t block_size = 4096;
-    static constexpr uint64_t fs_size = 2147483648;
-
-  protected:
-    void SetUp() override {
-        fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
-        uint64_t count = fs_size / block_size;
-        std::string dd_cmd =
-                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
-                                              " count=%" PRIu64 " > /dev/null 2>&1",
-                                              fs_path.c_str(), block_size, count);
-        std::string mkfs_cmd =
-                ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
-        // create mount point
-        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
-        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
-        // create file for the file system
-        int ret = system(dd_cmd.c_str());
-        ASSERT_EQ(ret, 0);
-        // Get and attach a loop device to the filesystem we created
-        LoopDevice loop_dev(fs_path);
-        ASSERT_TRUE(loop_dev.valid());
-        // create file system
-        ret = system(mkfs_cmd.c_str());
-        ASSERT_EQ(ret, 0);
-
-        // mount the file system
-        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
-    }
-
-    void TearDown() override {
-        umount(mntpoint.c_str());
-        rmdir(mntpoint.c_str());
-        unlink(fs_path.c_str());
-    }
-
-    std::string mntpoint;
-    std::string fs_path;
-};
-
-bool DetermineBlockSize() {
-    struct statfs s;
-    if (statfs(gTestDir.c_str(), &s)) {
-        std::cerr << "Could not call statfs: " << strerror(errno) << "\n";
-        return false;
-    }
-    if (!s.f_bsize) {
-        std::cerr << "Invalid block size: " << s.f_bsize << "\n";
-        return false;
-    }
-
-    gBlockSize = s.f_bsize;
-    return true;
-}
-
-}  // namespace fiemap_writer
-}  // namespace android
-
-using namespace android::fiemap_writer;
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    if (argc > 1 && argv[1] == "-h"s) {
-        cerr << "Usage: [test_dir] [file_size]\n";
-        cerr << "\n";
-        cerr << "Note: test_dir must be a writable, unencrypted directory.\n";
-        exit(EXIT_FAILURE);
-    }
-    ::android::base::InitLogging(argv, ::android::base::StderrLogger);
-
-    std::string root_dir = "/data/local/unencrypted";
-    if (access(root_dir.c_str(), F_OK)) {
-        root_dir = "/data";
-    }
-
-    std::string tempdir = root_dir + "/XXXXXX"s;
-    if (!mkdtemp(tempdir.data())) {
-        cerr << "unable to create tempdir on " << root_dir << "\n";
-        exit(EXIT_FAILURE);
-    }
-    if (!android::base::Realpath(tempdir, &gTestDir)) {
-        cerr << "unable to find realpath for " << tempdir;
-        exit(EXIT_FAILURE);
-    }
-
-    if (argc > 2) {
-        testfile_size = strtoull(argv[2], NULL, 0);
-        if (testfile_size == ULLONG_MAX) {
-            testfile_size = 512 * 1024 * 1024;
-        }
-    }
-
-    if (!DetermineBlockSize()) {
-        exit(EXIT_FAILURE);
-    }
-
-    auto result = RUN_ALL_TESTS();
-
-    std::string cmd = "rm -rf " + gTestDir;
-    system(cmd.c_str());
-
-    return result;
-}
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
deleted file mode 100644
index ee79262..0000000
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/fiemap_writer.h
+++ /dev/null
@@ -1,118 +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 <linux/fiemap.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <functional>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-namespace android {
-namespace fiemap_writer {
-
-class FiemapWriter;
-using FiemapUniquePtr = std::unique_ptr<FiemapWriter>;
-
-class FiemapWriter final {
-  public:
-    // Factory method for FiemapWriter.
-    // The method returns FiemapUniquePtr that contains all the data necessary to be able to write
-    // to the given file directly using raw block i/o. The optional progress callback will be
-    // invoked, if create is true, while the file is being initialized. It receives the bytes
-    // written and the number of total bytes. If the callback returns false, the operation will
-    // fail.
-    //
-    // Note: when create is true, the file size will be aligned up to the nearest file system
-    // block.
-    static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,
-                                bool create = true,
-                                std::function<bool(uint64_t, uint64_t)> progress = {});
-
-    // Check that a file still has the same extents since it was last opened with FiemapWriter,
-    // assuming the file was not resized outside of FiemapWriter. Returns false either on error
-    // or if the file was not pinned.
-    //
-    // This will always return true on Ext4. On F2FS, it will return true if either of the
-    // following cases are true:
-    //   - The file was never pinned.
-    //   - The file is pinned and has not been moved by the GC.
-    // Thus, this method should only be called for pinned files (such as those returned by
-    // FiemapWriter::Open).
-    static bool HasPinnedExtents(const std::string& file_path);
-
-    // Returns the underlying block device of a file. This will look past device-mapper layers
-    // as long as each layer would not change block mappings (i.e., dm-crypt, dm-bow, and dm-
-    // default-key tables are okay; dm-linear is not). If a mapping such as dm-linear is found,
-    // it will be returned in place of any physical block device.
-    //
-    // It is the caller's responsibility to check whether the returned block device is acceptable.
-    // Gsid, for example, will only accept /dev/block/by-name/userdata as the bottom device.
-    // Callers can check the device name (dm- or loop prefix), inspect sysfs, or compare the major
-    // number against a boot device.
-    //
-    // If device-mapper nodes were encountered, then |uses_dm| will be set to true.
-    static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
-                                      bool* uses_dm = nullptr);
-
-    ~FiemapWriter() = default;
-
-    const std::string& file_path() const { return file_path_; };
-    uint64_t size() const { return file_size_; };
-    const std::string& bdev_path() const { return bdev_path_; };
-    uint64_t block_size() const { return block_size_; };
-    const std::vector<struct fiemap_extent>& extents() { return extents_; };
-    uint32_t fs_type() const { return fs_type_; }
-
-    // Non-copyable & Non-movable
-    FiemapWriter(const FiemapWriter&) = delete;
-    FiemapWriter& operator=(const FiemapWriter&) = delete;
-    FiemapWriter& operator=(FiemapWriter&&) = delete;
-    FiemapWriter(FiemapWriter&&) = delete;
-
-  private:
-    // Name of the file managed by this class.
-    std::string file_path_;
-    // Block device on which we have created the file.
-    std::string bdev_path_;
-
-    // Size in bytes of the file this class is writing
-    uint64_t file_size_;
-
-    // total size in bytes of the block device
-    uint64_t bdev_size_;
-
-    // Filesystem type where the file is being created.
-    // See: <uapi/linux/magic.h> for filesystem magic numbers
-    uint32_t fs_type_;
-
-    // block size as reported by the kernel of the underlying block device;
-    uint64_t block_size_;
-
-    // This file's fiemap
-    std::vector<struct fiemap_extent> extents_;
-
-    FiemapWriter() = default;
-};
-
-}  // namespace fiemap_writer
-}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h b/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
deleted file mode 100644
index 7b977e1..0000000
--- a/fs_mgr/libfiemap_writer/include/libfiemap_writer/split_fiemap_writer.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-#include "fiemap_writer.h"
-
-namespace android {
-namespace fiemap_writer {
-
-// Wrapper around FiemapWriter that is able to split images across files if
-// necessary.
-class SplitFiemap final {
-  public:
-    using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;
-
-    // Create a new split fiemap file. If |max_piece_size| is 0, the number of
-    // pieces will be determined automatically by detecting the filesystem.
-    // Otherwise, the file will be split evenly (with the remainder in the
-    // final file).
-    static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,
-                                               uint64_t max_piece_size,
-                                               ProgressCallback progress = {});
-
-    // Open an existing split fiemap file.
-    static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);
-
-    ~SplitFiemap();
-
-    // Return a list of all files created for a split file.
-    static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);
-
-    // Destroy all components of a split file. If the root file does not exist,
-    // this returns true and does not report an error.
-    static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);
-
-    // Return whether all components of a split file still have pinned extents.
-    bool HasPinnedExtents() const;
-
-    // Helper method for writing data that spans files. Note there is no seek
-    // method (yet); this starts at 0 and increments the position by |bytes|.
-    bool Write(const void* data, uint64_t bytes);
-
-    // Flush all writes to all split files.
-    bool Flush();
-
-    const std::vector<struct fiemap_extent>& extents();
-    uint32_t block_size() const;
-    uint64_t size() const { return total_size_; }
-    const std::string& bdev_path() const;
-
-    // Non-copyable & Non-movable
-    SplitFiemap(const SplitFiemap&) = delete;
-    SplitFiemap& operator=(const SplitFiemap&) = delete;
-    SplitFiemap& operator=(SplitFiemap&&) = delete;
-    SplitFiemap(SplitFiemap&&) = delete;
-
-  private:
-    SplitFiemap() = default;
-    void AddFile(FiemapUniquePtr&& file);
-
-    bool creating_ = false;
-    std::string list_file_;
-    std::vector<FiemapUniquePtr> files_;
-    std::vector<struct fiemap_extent> extents_;
-    uint64_t total_size_ = 0;
-
-    // Most recently open file and position for Write().
-    size_t cursor_index_ = 0;
-    uint64_t cursor_file_pos_ = 0;
-    android::base::unique_fd cursor_fd_;
-};
-
-}  // namespace fiemap_writer
-}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp b/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
deleted file mode 100644
index a0ccc10..0000000
--- a/fs_mgr/libfiemap_writer/split_fiemap_writer.cpp
+++ /dev/null
@@ -1,297 +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 <libfiemap_writer/split_fiemap_writer.h>
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fiemap_writer {
-
-using android::base::unique_fd;
-
-// We use a four-digit suffix at the end of filenames.
-static const size_t kMaxFilePieces = 500;
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,
-                                                 uint64_t max_piece_size,
-                                                 ProgressCallback progress) {
-    if (!file_size) {
-        LOG(ERROR) << "Cannot create a fiemap for a 0-length file: " << file_path;
-        return nullptr;
-    }
-
-    if (!max_piece_size) {
-        max_piece_size = DetermineMaximumFileSize(file_path);
-        if (!max_piece_size) {
-            LOG(ERROR) << "Could not determine maximum file size for " << file_path;
-            return nullptr;
-        }
-    }
-
-    // Call |progress| only when the total percentage would significantly change.
-    int permille = -1;
-    uint64_t total_bytes_written = 0;
-    auto on_progress = [&](uint64_t written, uint64_t) -> bool {
-        uint64_t actual_written = total_bytes_written + written;
-        int new_permille = (actual_written * 1000) / file_size;
-        if (new_permille != permille && actual_written < file_size) {
-            if (progress && !progress(actual_written, file_size)) {
-                return false;
-            }
-            permille = new_permille;
-        }
-        return true;
-    };
-
-    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
-    out->creating_ = true;
-    out->list_file_ = file_path;
-
-    // Create the split files.
-    uint64_t remaining_bytes = file_size;
-    while (remaining_bytes) {
-        if (out->files_.size() >= kMaxFilePieces) {
-            LOG(ERROR) << "Requested size " << file_size << " created too many split files";
-            return nullptr;
-        }
-        std::string chunk_path =
-                android::base::StringPrintf("%s.%04d", file_path.c_str(), (int)out->files_.size());
-        uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);
-        auto writer = FiemapWriter::Open(chunk_path, chunk_size, true, on_progress);
-        if (!writer) {
-            return nullptr;
-        }
-
-        // To make sure the alignment doesn't create too much inconsistency, we
-        // account the *actual* size, not the requested size.
-        total_bytes_written += writer->size();
-        remaining_bytes -= writer->size();
-
-        out->AddFile(std::move(writer));
-    }
-
-    // Create the split file list.
-    unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));
-    if (fd < 0) {
-        PLOG(ERROR) << "Failed to open " << file_path;
-        return nullptr;
-    }
-
-    for (const auto& writer : out->files_) {
-        std::string line = android::base::Basename(writer->file_path()) + "\n";
-        if (!android::base::WriteFully(fd, line.data(), line.size())) {
-            PLOG(ERROR) << "Write failed " << file_path;
-            return nullptr;
-        }
-    }
-
-    // Unset this bit, so we don't unlink on destruction.
-    out->creating_ = false;
-    return out;
-}
-
-std::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {
-    std::vector<std::string> files;
-    if (!GetSplitFileList(file_path, &files)) {
-        return nullptr;
-    }
-
-    std::unique_ptr<SplitFiemap> out(new SplitFiemap());
-    out->list_file_ = file_path;
-
-    for (const auto& file : files) {
-        auto writer = FiemapWriter::Open(file, 0, false);
-        if (!writer) {
-            // Error was logged in Open().
-            return nullptr;
-        }
-        out->AddFile(std::move(writer));
-    }
-    return out;
-}
-
-bool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {
-    // This is not the most efficient thing, but it is simple and recovering
-    // the fiemap/fibmap is much more expensive.
-    std::string contents;
-    if (!android::base::ReadFileToString(file_path, &contents, true)) {
-        PLOG(ERROR) << "Error reading file: " << file_path;
-        return false;
-    }
-
-    std::vector<std::string> names = android::base::Split(contents, "\n");
-    std::string dir = android::base::Dirname(file_path);
-    for (const auto& name : names) {
-        if (!name.empty()) {
-            list->emplace_back(dir + "/" + name);
-        }
-    }
-    return true;
-}
-
-bool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {
-    // Early exit if this does not exist, and do not report an error.
-    if (access(file_path.c_str(), F_OK) && errno == ENOENT) {
-        return true;
-    }
-
-    bool ok = true;
-    std::vector<std::string> files;
-    if (GetSplitFileList(file_path, &files)) {
-        for (const auto& file : files) {
-            ok &= android::base::RemoveFileIfExists(file, message);
-        }
-    }
-    ok &= android::base::RemoveFileIfExists(file_path, message);
-    return ok;
-}
-
-bool SplitFiemap::HasPinnedExtents() const {
-    for (const auto& file : files_) {
-        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
-            return false;
-        }
-    }
-    return true;
-}
-
-const std::vector<struct fiemap_extent>& SplitFiemap::extents() {
-    if (extents_.empty()) {
-        for (const auto& file : files_) {
-            const auto& extents = file->extents();
-            extents_.insert(extents_.end(), extents.begin(), extents.end());
-        }
-    }
-    return extents_;
-}
-
-bool SplitFiemap::Write(const void* data, uint64_t bytes) {
-    // Open the current file.
-    FiemapWriter* file = files_[cursor_index_].get();
-
-    const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);
-    uint64_t bytes_remaining = bytes;
-    while (bytes_remaining) {
-        // How many bytes can we write into the current file?
-        uint64_t file_bytes_left = file->size() - cursor_file_pos_;
-        if (!file_bytes_left) {
-            if (cursor_index_ == files_.size() - 1) {
-                LOG(ERROR) << "write past end of file requested";
-                return false;
-            }
-
-            // No space left in the current file, but we have more files to
-            // use, so prep the next one.
-            cursor_fd_ = {};
-            cursor_file_pos_ = 0;
-            file = files_[++cursor_index_].get();
-            file_bytes_left = file->size();
-        }
-
-        // Open the current file if it's not open.
-        if (cursor_fd_ < 0) {
-            cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));
-            if (cursor_fd_ < 0) {
-                PLOG(ERROR) << "open failed: " << file->file_path();
-                return false;
-            }
-            CHECK(cursor_file_pos_ == 0);
-        }
-
-        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {
-            LOG(ERROR) << "file is no longer pinned: " << file->file_path();
-            return false;
-        }
-
-        uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);
-        if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {
-            PLOG(ERROR) << "write failed: " << file->file_path();
-            return false;
-        }
-        data_ptr += bytes_to_write;
-        bytes_remaining -= bytes_to_write;
-        cursor_file_pos_ += bytes_to_write;
-    }
-
-    // If we've reached the end of the current file, close it for sanity.
-    if (cursor_file_pos_ == file->size()) {
-        cursor_fd_ = {};
-    }
-    return true;
-}
-
-bool SplitFiemap::Flush() {
-    for (const auto& file : files_) {
-        unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));
-        if (fd < 0) {
-            PLOG(ERROR) << "open failed: " << file->file_path();
-            return false;
-        }
-        if (fsync(fd)) {
-            PLOG(ERROR) << "fsync failed: " << file->file_path();
-            return false;
-        }
-    }
-    return true;
-}
-
-SplitFiemap::~SplitFiemap() {
-    if (!creating_) {
-        return;
-    }
-
-    // We failed to finish creating, so unlink everything.
-    unlink(list_file_.c_str());
-    for (auto&& file : files_) {
-        std::string path = file->file_path();
-        file = nullptr;
-
-        unlink(path.c_str());
-    }
-}
-
-void SplitFiemap::AddFile(FiemapUniquePtr&& file) {
-    total_size_ += file->size();
-    files_.emplace_back(std::move(file));
-}
-
-uint32_t SplitFiemap::block_size() const {
-    return files_[0]->block_size();
-}
-
-const std::string& SplitFiemap::bdev_path() const {
-    return files_[0]->bdev_path();
-}
-
-}  // namespace fiemap_writer
-}  // namespace android
diff --git a/fs_mgr/libfiemap_writer/testdata/file_32k b/fs_mgr/libfiemap_writer/testdata/file_32k
deleted file mode 100644
index 12f3be4..0000000
--- a/fs_mgr/libfiemap_writer/testdata/file_32k
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/file_4k b/fs_mgr/libfiemap_writer/testdata/file_4k
deleted file mode 100644
index 08e7df1..0000000
--- a/fs_mgr/libfiemap_writer/testdata/file_4k
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/testdata/unaligned_file b/fs_mgr/libfiemap_writer/testdata/unaligned_file
deleted file mode 100644
index c107c26..0000000
--- a/fs_mgr/libfiemap_writer/testdata/unaligned_file
+++ /dev/null
Binary files differ
diff --git a/fs_mgr/libfiemap_writer/utility.cpp b/fs_mgr/libfiemap_writer/utility.cpp
deleted file mode 100644
index 192ec16..0000000
--- a/fs_mgr/libfiemap_writer/utility.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "utility.h"
-
-#include <stdint.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <libfiemap_writer/fiemap_writer.h>
-
-namespace android {
-namespace fiemap_writer {
-
-uint64_t DetermineMaximumFileSize(const std::string& file_path) {
-    // Create the smallest file possible (one block).
-    auto writer = FiemapWriter::Open(file_path, 1);
-    if (!writer) {
-        return 0;
-    }
-
-    uint64_t result = 0;
-    switch (writer->fs_type()) {
-        case EXT4_SUPER_MAGIC:
-            // The minimum is 16GiB, so just report that. If we wanted we could parse the
-            // superblock and figure out if 64-bit support is enabled.
-            result = 17179869184ULL;
-            break;
-        case F2FS_SUPER_MAGIC:
-            // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
-            // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
-            result = 4329690886144ULL;
-            break;
-        case MSDOS_SUPER_MAGIC:
-            // 4GB-1, which we want aligned to the block size.
-            result = 4294967295;
-            result -= (result % writer->block_size());
-            break;
-        default:
-            LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
-            break;
-    }
-
-    // Close and delete the temporary file.
-    writer = nullptr;
-    unlink(file_path.c_str());
-
-    return result;
-}
-
-}  // namespace fiemap_writer
-}  // namespace android
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index a3c76ab..414a186 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -61,6 +61,7 @@
         "libavb",
         "libavb_host_sysdeps",
         "libdm",
+        "libext2_uuid",
         "libfs_avb",
         "libfstab",
         "libgtest_host",
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index d9650f3..4505382 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -104,31 +104,23 @@
     }
     table.set_readonly(true);
 
+    std::chrono::milliseconds timeout = {};
+    if (wait_for_verity_dev) timeout = 1s;
+
+    std::string dev_path;
     const std::string mount_point(Basename(fstab_entry->mount_point));
     const std::string device_name(GetVerityDeviceName(*fstab_entry));
     android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
-    if (!dm.CreateDevice(device_name, table)) {
+    if (!dm.CreateDevice(device_name, table, &dev_path, timeout)) {
         LERROR << "Couldn't create verity device!";
         return false;
     }
 
-    std::string dev_path;
-    if (!dm.GetDmDevicePathByName(device_name, &dev_path)) {
-        LERROR << "Couldn't get verity device path!";
-        return false;
-    }
-
     // Marks the underlying block device as read-only.
     SetBlockDeviceReadOnly(fstab_entry->blk_device);
 
     // Updates fstab_rec->blk_device to verity device name.
     fstab_entry->blk_device = dev_path;
-
-    // Makes sure we've set everything up properly.
-    if (wait_for_verity_dev && !WaitForFile(dev_path, 1s)) {
-        return false;
-    }
-
     return true;
 }
 
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index b504161..f0142bb 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -36,6 +36,7 @@
         "builder.cpp",
         "images.cpp",
         "partition_opener.cpp",
+        "property_fetcher.cpp",
         "reader.cpp",
         "utility.cpp",
         "writer.cpp",
@@ -54,13 +55,14 @@
     export_include_dirs: ["include"],
 }
 
-cc_test {
-    name: "liblp_test_static",
+cc_defaults {
+    name: "liblp_test_defaults",
     defaults: ["fs_mgr_defaults"],
     cppflags: [
         "-Wno-unused-parameter",
     ],
     static_libs: [
+        "libcutils",
         "libgmock",
         "libfs_mgr",
         "liblp",
@@ -68,15 +70,25 @@
     stl: "libc++_static",
     srcs: [
         "builder_test.cpp",
+        "device_test.cpp",
         "io_test.cpp",
         "test_partition_opener.cpp",
         "utility_test.cpp",
     ],
-    target: {
-        android: {
-            static_libs: [
-                "libcutils",
-            ],
-        },
-    },
 }
+
+cc_test {
+    name: "liblp_test",
+    defaults: ["liblp_test_defaults"],
+    test_config: "liblp_test.xml",
+    test_suites: [
+        "device-tests",
+        "vts-core",
+    ],
+}
+
+cc_test {
+    name: "vts_kernel_liblp_test",
+    defaults: ["liblp_test_defaults"],
+}
+
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
index fe1002c..2eb0ad1 100644
--- a/fs_mgr/liblp/AndroidTest.xml
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -21,8 +21,8 @@
     </target_preparer>
     <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
       <option name="test-module-name" value="VtsKernelLiblpTest"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/liblp_test_static/liblp_test_static" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/liblp_test_static/liblp_test_static" />
+        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_kernel_liblp_test/vts_kernel_liblp_test" />
+        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_kernel_liblp_test/vts_kernel_liblp_test" />
         <option name="binary-test-type" value="gtest"/>
         <option name="test-timeout" value="1m"/>
         <option name="precondition-first-api-level" value="29" />
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
new file mode 100644
index 0000000..04bcbda
--- /dev/null
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "liblp_test"
+    }
+  ]
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 25a042f..77c9c28 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -20,21 +20,16 @@
 
 #include <algorithm>
 
-#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
 
 #include "liblp/liblp.h"
+#include "liblp/property_fetcher.h"
 #include "reader.h"
 #include "utility.h"
 
 namespace android {
 namespace fs_mgr {
 
-bool MetadataBuilder::sABOverrideSet;
-bool MetadataBuilder::sABOverrideValue;
-
-static const std::string kDefaultGroup = "default";
-
 bool LinearExtent::AddTo(LpMetadata* out) const {
     if (device_index_ >= out->block_devices.size()) {
         LERROR << "Extent references unknown block device.";
@@ -145,7 +140,7 @@
     }
     if (opener) {
         for (size_t i = 0; i < builder->block_devices_.size(); i++) {
-            std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+            std::string partition_name = builder->GetBlockDevicePartitionName(i);
             BlockDeviceInfo device_info;
             if (opener->GetInfo(partition_name, &device_info)) {
                 builder->UpdateBlockDeviceInfo(i, device_info);
@@ -164,15 +159,34 @@
         return nullptr;
     }
 
-    // On non-retrofit devices there is only one location for metadata: the
-    // super partition. update_engine will remove and resize partitions as
-    // needed. On the other hand, for retrofit devices, we'll need to
-    // translate block device and group names to update their slot suffixes.
+    // On retrofit DAP devices, modify the metadata so that it is suitable for being written
+    // to the target slot later. We detect retrofit DAP devices by checking the super partition
+    // name and system properties.
+    // See comments for UpdateMetadataForOtherSuper.
     auto super_device = GetMetadataSuperBlockDevice(*metadata.get());
-    if (GetBlockDevicePartitionName(*super_device) == "super") {
-        return New(*metadata.get(), &opener);
+    if (android::fs_mgr::GetBlockDevicePartitionName(*super_device) != "super" &&
+        IsRetrofitDynamicPartitionsDevice()) {
+        if (!UpdateMetadataForOtherSuper(metadata.get(), source_slot_number, target_slot_number)) {
+            return nullptr;
+        }
     }
 
+    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false)) {
+        if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,
+                                              target_slot_number)) {
+            return nullptr;
+        }
+    }
+
+    return New(*metadata.get(), &opener);
+}
+
+// For retrofit DAP devices, there are (conceptually) two super partitions. We'll need to translate
+// block device and group names to update their slot suffixes.
+// (On the other hand, On non-retrofit DAP devices there is only one location for metadata: the
+// super partition. update_engine will remove and resize partitions as needed.)
+bool MetadataBuilder::UpdateMetadataForOtherSuper(LpMetadata* metadata, uint32_t source_slot_number,
+                                                  uint32_t target_slot_number) {
     // Clear partitions and extents, since they have no meaning on the target
     // slot. We also clear groups since they are re-added during OTA.
     metadata->partitions.clear();
@@ -185,14 +199,15 @@
     // Translate block devices.
     auto source_block_devices = std::move(metadata->block_devices);
     for (const auto& source_block_device : source_block_devices) {
-        std::string partition_name = GetBlockDevicePartitionName(source_block_device);
+        std::string partition_name =
+                android::fs_mgr::GetBlockDevicePartitionName(source_block_device);
         std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
         if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {
             // This should never happen. It means that the source metadata
             // refers to a target or unknown block device.
             LERROR << "Invalid block device for slot " << source_slot_suffix << ": "
                    << partition_name;
-            return nullptr;
+            return false;
         }
         std::string new_name =
                 partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
@@ -201,17 +216,12 @@
         auto new_device = source_block_device;
         if (!UpdateBlockDevicePartitionName(&new_device, new_name)) {
             LERROR << "Partition name too long: " << new_name;
-            return nullptr;
+            return false;
         }
         metadata->block_devices.emplace_back(new_device);
     }
 
-    return New(*metadata.get(), &opener);
-}
-
-void MetadataBuilder::OverrideABForTesting(bool ab_device) {
-    sABOverrideSet = true;
-    sABOverrideValue = ab_device;
+    return true;
 }
 
 MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
@@ -373,7 +383,7 @@
             block_devices_.emplace_back(out);
         }
     }
-    if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) {
+    if (GetBlockDevicePartitionName(0) != super_partition) {
         LERROR << "No super partition was specified.";
         return false;
     }
@@ -410,7 +420,7 @@
     geometry_.metadata_slot_count = metadata_slot_count;
     geometry_.logical_block_size = logical_block_size;
 
-    if (!AddGroup(kDefaultGroup, 0)) {
+    if (!AddGroup(std::string(kDefaultGroup), 0)) {
         return false;
     }
     return true;
@@ -426,7 +436,7 @@
 }
 
 Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
-    return AddPartition(name, kDefaultGroup, attributes);
+    return AddPartition(name, std::string(kDefaultGroup), attributes);
 }
 
 Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
@@ -456,7 +466,7 @@
     return nullptr;
 }
 
-PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) {
+PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) {
     for (const auto& group : groups_) {
         if (group->name() == group_name) {
             return group.get();
@@ -580,7 +590,7 @@
     CHECK_NE(sectors_per_block, 0);
     CHECK(sectors_needed % sectors_per_block == 0);
 
-    if (IsABDevice() && !IsRetrofitDevice() && GetPartitionSlotSuffix(partition->name()) == "_b") {
+    if (IsABDevice() && ShouldHalveSuper() && GetPartitionSlotSuffix(partition->name()) == "_b") {
         // Allocate "a" partitions top-down and "b" partitions bottom-up, to
         // minimize fragmentation during OTA.
         free_regions = PrioritizeSecondHalfOfSuper(free_regions);
@@ -846,7 +856,7 @@
 bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,
                                             uint32_t* index) const {
     for (size_t i = 0; i < block_devices_.size(); i++) {
-        if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) {
+        if (GetBlockDevicePartitionName(i) == partition_name) {
             *index = i;
             return true;
         }
@@ -971,7 +981,8 @@
     // Note: we don't compare alignment, since it's a performance thing and
     // won't affect whether old extents continue to work.
     return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&
-           GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
+           android::fs_mgr::GetBlockDevicePartitionName(first) ==
+                   android::fs_mgr::GetBlockDevicePartitionName(second);
 }
 
 bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
@@ -1044,15 +1055,18 @@
     auto_slot_suffixing_ = true;
 }
 
-bool MetadataBuilder::IsABDevice() const {
-    if (sABOverrideSet) {
-        return sABOverrideValue;
-    }
-    return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
+bool MetadataBuilder::IsABDevice() {
+    return !IPropertyFetcher::GetInstance()->GetProperty("ro.boot.slot_suffix", "").empty();
 }
 
-bool MetadataBuilder::IsRetrofitDevice() const {
-    return GetBlockDevicePartitionName(block_devices_[0]) != LP_METADATA_DEFAULT_PARTITION_NAME;
+bool MetadataBuilder::IsRetrofitDynamicPartitionsDevice() {
+    return IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions_retrofit",
+                                                            false);
+}
+
+bool MetadataBuilder::ShouldHalveSuper() const {
+    return GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
+           !IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false);
 }
 
 bool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -1078,7 +1092,7 @@
     return partitions;
 }
 
-bool MetadataBuilder::ChangePartitionGroup(Partition* partition, const std::string& group_name) {
+bool MetadataBuilder::ChangePartitionGroup(Partition* partition, std::string_view group_name) {
     if (!FindGroup(group_name)) {
         LERROR << "Partition cannot change to unknown group: " << group_name;
         return false;
@@ -1116,5 +1130,11 @@
     return true;
 }
 
+std::string MetadataBuilder::GetBlockDevicePartitionName(uint64_t index) const {
+    return index < block_devices_.size()
+                   ? android::fs_mgr::GetBlockDevicePartitionName(block_devices_[index])
+                   : "";
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 34c68d4..dde6d07 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,34 +14,33 @@
  * limitations under the License.
  */
 
-#include <fs_mgr.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
 
+#include "liblp_test.h"
 #include "utility.h"
 
 using namespace std;
 using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
+using ::testing::_;
+using ::testing::AnyNumber;
 using ::testing::ElementsAre;
+using ::testing::NiceMock;
+using ::testing::Return;
 
 class Environment : public ::testing::Environment {
   public:
-    void SetUp() override { MetadataBuilder::OverrideABForTesting(false); }
+    void SetUp() override { ResetMockPropertyFetcher(); }
 };
 
 int main(int argc, char** argv) {
-    std::unique_ptr<Environment> env(new Environment);
-    ::testing::AddGlobalTestEnvironment(env.get());
     ::testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
 }
 
-class BuilderTest : public ::testing::Test {
-  public:
-    void SetUp() override { MetadataBuilder::OverrideABForTesting(false); }
-    void TearDown() override { MetadataBuilder::OverrideABForTesting(false); }
-};
+class BuilderTest : public LiblpTest {};
 
 TEST_F(BuilderTest, BuildBasic) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
@@ -448,23 +447,6 @@
     EXPECT_EQ(builder, nullptr);
 }
 
-TEST_F(BuilderTest, block_device_info) {
-    PartitionOpener opener;
-
-    BlockDeviceInfo device_info;
-    ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
-
-    // Sanity check that the device doesn't give us some weird inefficient
-    // alignment.
-    ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
-    ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
-    ASSERT_LE(device_info.alignment_offset, INT_MAX);
-    ASSERT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
-
-    // Having an alignment offset > alignment doesn't really make sense.
-    ASSERT_LT(device_info.alignment_offset, device_info.alignment);
-}
-
 TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
     BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
@@ -777,7 +759,9 @@
 
     // A and B slots should be allocated from separate halves of the partition,
     // to mitigate allocating too many extents. (b/120433288)
-    MetadataBuilder::OverrideABForTesting(true);
+    ON_CALL(*GetMockedPropertyFetcher(), GetProperty("ro.boot.slot_suffix", _))
+            .WillByDefault(Return("_a"));
+
     auto builder = MetadataBuilder::New(device_info, 65536, 2);
     ASSERT_NE(builder, nullptr);
     Partition* system_a = builder->AddPartition("system_a", 0);
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
new file mode 100644
index 0000000..382d53d
--- /dev/null
+++ b/fs_mgr/liblp/device_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/properties.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+#include <liblp/liblp.h>
+#include <liblp/metadata_format.h>
+#include <liblp/partition_opener.h>
+#include <liblp/property_fetcher.h>
+
+#include "liblp_test.h"
+
+using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
+using ::testing::Return;
+
+// Compliance test on the actual device with dynamic partitions.
+class DeviceTest : public LiblpTest {
+  public:
+    void SetUp() override {
+        // Read real properties.
+        IPropertyFetcher::OverrideForTesting(std::make_unique<PropertyFetcher>());
+        if (!IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions",
+                                                              false)) {
+            GTEST_SKIP() << "Device doesn't have dynamic partitions enabled, skipping";
+        }
+    }
+};
+
+TEST_F(DeviceTest, BlockDeviceInfo) {
+    PartitionOpener opener;
+    BlockDeviceInfo device_info;
+    ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
+
+    // Sanity check that the device doesn't give us some weird inefficient
+    // alignment.
+    EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
+    EXPECT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
+    EXPECT_LE(device_info.alignment_offset, INT_MAX);
+    EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
+
+    // Having an alignment offset > alignment doesn't really make sense.
+    EXPECT_LT(device_info.alignment_offset, device_info.alignment);
+}
+
+TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
+    auto slot_suffix = fs_mgr_get_slot_suffix();
+    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    auto super_name = fs_mgr_get_super_partition_name(slot_number);
+    auto metadata = ReadMetadata(super_name, slot_number);
+    EXPECT_NE(metadata, nullptr);
+}
+
+TEST_F(DeviceTest, ReadSuperPartitionOtherSlot) {
+    auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
+    if (other_slot_suffix.empty()) {
+        GTEST_SKIP() << "No other slot, skipping";
+    }
+    if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions_retrofit",
+                                                         false)) {
+        GTEST_SKIP() << "Device with retrofit dynamic partition may not have metadata at other "
+                     << "slot, skipping";
+    }
+
+    auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
+    auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
+    auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
+    EXPECT_NE(other_metadata, nullptr);
+}
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index e70c552..c3f6e91 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -24,6 +24,7 @@
 #include <memory>
 #include <optional>
 #include <set>
+#include <string_view>
 
 #include "liblp.h"
 #include "partition_opener.h"
@@ -37,6 +38,9 @@
 static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
 static const uint32_t kDefaultBlockSize = 4096;
 
+// Name of the default group in a metadata.
+static constexpr std::string_view kDefaultGroup = "default";
+
 // Abstraction around dm-targets that can be encoded into logical partition tables.
 class Extent {
   public:
@@ -124,7 +128,7 @@
 
   private:
     void ShrinkTo(uint64_t aligned_size);
-    void set_group_name(const std::string& group_name) { group_name_ = group_name; }
+    void set_group_name(std::string_view group_name) { group_name_ = group_name; }
 
     std::string name_;
     std::string group_name_;
@@ -196,9 +200,6 @@
         return New(device_info, metadata_max_size, metadata_slot_count);
     }
 
-    // Used by the test harness to override whether the device is "A/B".
-    static void OverrideABForTesting(bool ab_device);
-
     // Define a new partition group. By default there is one group called
     // "default", with an unrestricted size. A non-zero size will restrict the
     // total space used by all partitions in the group.
@@ -226,7 +227,7 @@
     Partition* FindPartition(const std::string& name);
 
     // Find a group by name. If no group is found, nullptr is returned.
-    PartitionGroup* FindGroup(const std::string& name);
+    PartitionGroup* FindGroup(std::string_view name);
 
     // Add a predetermined extent to a partition.
     bool AddLinearExtent(Partition* partition, const std::string& block_device,
@@ -251,7 +252,7 @@
     // the metadata is exported, to avoid errors during potential group and
     // size shuffling operations. This will return false if the new group does
     // not exist.
-    bool ChangePartitionGroup(Partition* partition, const std::string& group_name);
+    bool ChangePartitionGroup(Partition* partition, std::string_view group_name);
 
     // Changes the size of a partition group. Size constraints will not be
     // checked until metadata is exported, to avoid errors during group
@@ -286,6 +287,9 @@
     // Return true if a block device is found, else false.
     bool HasBlockDevice(const std::string& partition_name) const;
 
+    // Return the name of the block device at |index|.
+    std::string GetBlockDevicePartitionName(uint64_t index) const;
+
   private:
     MetadataBuilder();
     MetadataBuilder(const MetadataBuilder&) = delete;
@@ -306,8 +310,16 @@
     void ImportExtents(Partition* dest, const LpMetadata& metadata,
                        const LpMetadataPartition& source);
     bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
-    bool IsABDevice() const;
-    bool IsRetrofitDevice() const;
+
+    // Return true if the device is an AB device.
+    static bool IsABDevice();
+
+    // Return true if the device is retrofitting dynamic partitions.
+    static bool IsRetrofitDynamicPartitionsDevice();
+
+    // Return true if _b partitions should be prioritized at the second half of the device.
+    bool ShouldHalveSuper() const;
+
     bool ValidatePartitionGroups() const;
 
     struct Interval {
@@ -336,8 +348,8 @@
                                                     const std::vector<Interval>& free_list,
                                                     uint64_t sectors_needed) const;
 
-    static bool sABOverrideValue;
-    static bool sABOverrideSet;
+    static bool UpdateMetadataForOtherSuper(LpMetadata* metadata, uint32_t source_slot_number,
+                                            uint32_t target_slot_number);
 
     LpMetadataGeometry geometry_;
     LpMetadataHeader header_;
diff --git a/fs_mgr/liblp/include/liblp/mock_property_fetcher.h b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
new file mode 100644
index 0000000..f15611b
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include <liblp/property_fetcher.h>
+
+namespace android {
+namespace fs_mgr {
+namespace testing {
+
+class MockPropertyFetcher : public IPropertyFetcher {
+  public:
+    MOCK_METHOD2(GetProperty, std::string(const std::string&, const std::string&));
+    MOCK_METHOD2(GetBoolProperty, bool(const std::string&, bool));
+
+    // By default, return default_value for all functions.
+    MockPropertyFetcher() {
+        using ::testing::_;
+        using ::testing::Invoke;
+        ON_CALL(*this, GetProperty(_, _)).WillByDefault(Invoke([](const auto&, const auto& def) {
+            return def;
+        }));
+        ON_CALL(*this, GetBoolProperty(_, _)).WillByDefault(Invoke([](const auto&, auto def) {
+            return def;
+        }));
+    }
+};
+
+static inline void ResetMockPropertyFetcher() {
+    IPropertyFetcher::OverrideForTesting(
+            std::make_unique<::testing::NiceMock<MockPropertyFetcher>>());
+}
+
+static inline MockPropertyFetcher* GetMockedPropertyFetcher() {
+    return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
+}
+
+}  // namespace testing
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
index e506bd5..7c9100b 100644
--- a/fs_mgr/liblp/include/liblp/partition_opener.h
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -62,6 +62,11 @@
     // Return block device information about the given named physical partition.
     // The name can be an absolute path if the full path is already known.
     virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;
+
+    // Return a path that can be used to pass the block device to device-mapper.
+    // This must either result in an absolute path, or a major:minor device
+    // sequence.
+    virtual std::string GetDeviceString(const std::string& partition_name) const = 0;
 };
 
 // Helper class to implement IPartitionOpener. If |partition_name| is not an
@@ -71,6 +76,7 @@
     virtual android::base::unique_fd Open(const std::string& partition_name,
                                           int flags) const override;
     virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+    virtual std::string GetDeviceString(const std::string& partition_name) const override;
 };
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/liblp/include/liblp/property_fetcher.h b/fs_mgr/liblp/include/liblp/property_fetcher.h
new file mode 100644
index 0000000..e73a1f5
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/property_fetcher.h
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <memory>
+
+namespace android {
+namespace fs_mgr {
+
+class IPropertyFetcher {
+  public:
+    virtual ~IPropertyFetcher() = default;
+    virtual std::string GetProperty(const std::string& key, const std::string& defaultValue) = 0;
+    virtual bool GetBoolProperty(const std::string& key, bool defaultValue) = 0;
+
+    static IPropertyFetcher* GetInstance();
+    static void OverrideForTesting(std::unique_ptr<IPropertyFetcher>&&);
+};
+
+class PropertyFetcher : public IPropertyFetcher {
+  public:
+    ~PropertyFetcher() = default;
+    std::string GetProperty(const std::string& key, const std::string& defaultValue) override;
+    bool GetBoolProperty(const std::string& key, bool defaultValue) override;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index fcef1f0..df89e9c 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -21,12 +21,12 @@
 
 #include <android-base/file.h>
 #include <android-base/unique_fd.h>
-#include <fs_mgr.h>
-#include <fstab/fstab.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
 
 #include "images.h"
+#include "liblp_test.h"
 #include "reader.h"
 #include "test_partition_opener.h"
 #include "utility.h"
@@ -34,6 +34,9 @@
 
 using namespace std;
 using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
+using ::testing::_;
+using ::testing::Return;
 using unique_fd = android::base::unique_fd;
 
 // Our tests assume a 128KiB disk with two 512 byte metadata slots.
@@ -120,7 +123,7 @@
 }
 
 // Test that our CreateFakeDisk() function works.
-TEST(liblp, CreateFakeDisk) {
+TEST_F(LiblpTest, CreateFakeDisk) {
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
@@ -136,7 +139,7 @@
 
 // Flashing metadata should not work if the metadata was created for a larger
 // disk than the destination disk.
-TEST(liblp, ExportDiskTooSmall) {
+TEST_F(LiblpTest, ExportDiskTooSmall) {
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
     ASSERT_NE(builder, nullptr);
     unique_ptr<LpMetadata> exported = builder->Export();
@@ -153,7 +156,7 @@
 }
 
 // Test the basics of flashing a partition and reading it back.
-TEST(liblp, FlashAndReadback) {
+TEST_F(LiblpTest, FlashAndReadback) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -203,7 +206,7 @@
 }
 
 // Test that we can update metadata slots without disturbing others.
-TEST(liblp, UpdateAnyMetadataSlot) {
+TEST_F(LiblpTest, UpdateAnyMetadataSlot) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -248,7 +251,7 @@
     }
 }
 
-TEST(liblp, InvalidMetadataSlot) {
+TEST_F(LiblpTest, InvalidMetadataSlot) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -267,7 +270,7 @@
 
 // Test that updating a metadata slot does not allow it to be computed based
 // on mismatching geometry.
-TEST(liblp, NoChangingGeometry) {
+TEST_F(LiblpTest, NoChangingGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -296,7 +299,7 @@
 }
 
 // Test that changing one bit of metadata is enough to break the checksum.
-TEST(liblp, BitFlipGeometry) {
+TEST_F(LiblpTest, BitFlipGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -315,7 +318,7 @@
     EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
 }
 
-TEST(liblp, ReadBackupGeometry) {
+TEST_F(LiblpTest, ReadBackupGeometry) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -335,7 +338,7 @@
     EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
 }
 
-TEST(liblp, ReadBackupMetadata) {
+TEST_F(LiblpTest, ReadBackupMetadata) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -362,7 +365,7 @@
 
 // Test that we don't attempt to write metadata if it would overflow its
 // reserved space.
-TEST(liblp, TooManyPartitions) {
+TEST_F(LiblpTest, TooManyPartitions) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
 
@@ -416,7 +419,7 @@
 }
 
 // Test that we can read and write image files.
-TEST(liblp, ImageFiles) {
+TEST_F(LiblpTest, ImageFiles) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -432,7 +435,7 @@
 }
 
 // Test that we can read images from buffers.
-TEST(liblp, ImageFilesInMemory) {
+TEST_F(LiblpTest, ImageFilesInMemory) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -492,7 +495,7 @@
 
 // Test that an interrupted flash operation on the "primary" copy of metadata
 // is not fatal.
-TEST(liblp, UpdatePrimaryMetadataFailure) {
+TEST_F(LiblpTest, UpdatePrimaryMetadataFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -520,7 +523,7 @@
 
 // Test that an interrupted flash operation on the "backup" copy of metadata
 // is not fatal.
-TEST(liblp, UpdateBackupMetadataFailure) {
+TEST_F(LiblpTest, UpdateBackupMetadataFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -549,7 +552,7 @@
 // Test that an interrupted write *in between* writing metadata will read
 // the correct metadata copy. The primary is always considered newer than
 // the backup.
-TEST(liblp, UpdateMetadataCleanFailure) {
+TEST_F(LiblpTest, UpdateMetadataCleanFailure) {
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -586,7 +589,7 @@
 }
 
 // Test that writing a sparse image can be read back.
-TEST(liblp, FlashSparseImage) {
+TEST_F(LiblpTest, FlashSparseImage) {
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
@@ -620,7 +623,7 @@
     ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
 }
 
-TEST(liblp, AutoSlotSuffixing) {
+TEST_F(LiblpTest, AutoSlotSuffixing) {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -663,7 +666,10 @@
     EXPECT_EQ(metadata->groups[1].flags, 0);
 }
 
-TEST(liblp, UpdateRetrofit) {
+TEST_F(LiblpTest, UpdateRetrofit) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+            .WillByDefault(Return(true));
+
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     ASSERT_NE(builder, nullptr);
     ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -692,7 +698,10 @@
     ASSERT_TRUE(updated->extents.empty());
 }
 
-TEST(liblp, UpdateNonRetrofit) {
+TEST_F(LiblpTest, UpdateNonRetrofit) {
+    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+            .WillByDefault(Return(false));
+
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
@@ -704,19 +713,3 @@
     ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
     EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
 }
-
-TEST(liblp, ReadSuperPartition) {
-    auto slot_suffix = fs_mgr_get_slot_suffix();
-    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
-    auto super_name = fs_mgr_get_super_partition_name(slot_number);
-    auto metadata = ReadMetadata(super_name, slot_number);
-    ASSERT_NE(metadata, nullptr);
-
-    if (!slot_suffix.empty()) {
-        auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
-        auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
-        auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
-        auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
-        ASSERT_NE(other_metadata, nullptr);
-    }
-}
diff --git a/fs_mgr/libfiemap_writer/utility.h b/fs_mgr/liblp/liblp_test.h
similarity index 67%
rename from fs_mgr/libfiemap_writer/utility.h
rename to fs_mgr/liblp/liblp_test.h
index 2d418da..c61aaa5 100644
--- a/fs_mgr/libfiemap_writer/utility.h
+++ b/fs_mgr/liblp/liblp_test.h
@@ -16,15 +16,20 @@
 
 #pragma once
 
-#include <string>
+#include <gtest/gtest.h>
+
+#include <liblp/mock_property_fetcher.h>
 
 namespace android {
-namespace fiemap_writer {
+namespace fs_mgr {
+namespace testing {
 
-// Given a file that will be created, determine the maximum size its containing
-// filesystem allows. Note this is a theoretical maximum size; free space is
-// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
+class LiblpTest : public ::testing::Test {
+  public:
+    void SetUp() override { ResetMockPropertyFetcher(); }
+    void TearDown() override { ResetMockPropertyFetcher(); }
+};
 
-}  // namespace fiemap_writer
+}  // namespace testing
+}  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
new file mode 100644
index 0000000..d9ee12e
--- /dev/null
+++ b/fs_mgr/liblp/liblp_test.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for liblp_test">
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="vts-core" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="liblp_test" />
+    </test>
+</configuration>
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index cc4a882..1d4db85 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -41,7 +41,21 @@
     if (android::base::StartsWith(path, "/")) {
         return path;
     }
-    return "/dev/block/by-name/" + path;
+
+    auto by_name = "/dev/block/by-name/" + path;
+    if (access(by_name.c_str(), F_OK) != 0) {
+        // If the by-name symlink doesn't exist, as a special case we allow
+        // certain devices to be used as partition names. This can happen if a
+        // Dynamic System Update is installed to an sdcard, which won't be in
+        // the boot device list.
+        //
+        // We whitelist because most devices in /dev/block are not valid for
+        // storing fiemaps.
+        if (android::base::StartsWith(path, "mmcblk")) {
+            return "/dev/block/" + path;
+        }
+    }
+    return by_name;
 }
 
 bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
@@ -100,5 +114,9 @@
     return GetBlockDeviceInfo(path, info);
 }
 
+std::string PartitionOpener::GetDeviceString(const std::string& partition_name) const {
+    return GetPartitionAbsolutePath(partition_name);
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/property_fetcher.cpp b/fs_mgr/liblp/property_fetcher.cpp
new file mode 100644
index 0000000..038ef4d
--- /dev/null
+++ b/fs_mgr/liblp/property_fetcher.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "liblp/property_fetcher.h"
+
+#include <memory>
+
+#include <android-base/properties.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::string PropertyFetcher::GetProperty(const std::string& key, const std::string& default_value) {
+    return android::base::GetProperty(key, default_value);
+}
+
+bool PropertyFetcher::GetBoolProperty(const std::string& key, bool default_value) {
+    return android::base::GetBoolProperty(key, default_value);
+}
+
+static std::unique_ptr<IPropertyFetcher>* GetInstanceAllocation() {
+    static std::unique_ptr<IPropertyFetcher> instance = std::make_unique<PropertyFetcher>();
+    return &instance;
+}
+
+IPropertyFetcher* IPropertyFetcher::GetInstance() {
+    return GetInstanceAllocation()->get();
+}
+
+void IPropertyFetcher::OverrideForTesting(std::unique_ptr<IPropertyFetcher>&& fetcher) {
+    GetInstanceAllocation()->swap(fetcher);
+    fetcher.reset();
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index dcee6d2..8dbe955 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -18,6 +18,7 @@
 
 #include <stddef.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <functional>
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 338b525..17f05aa 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -24,6 +24,10 @@
 #include <sys/ioctl.h>
 #endif
 
+#include <map>
+#include <string>
+#include <vector>
+
 #include <android-base/file.h>
 #include <ext4_utils/ext4_utils.h>
 #include <openssl/sha.h>
@@ -182,6 +186,14 @@
     return true;
 }
 
+bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name) {
+    if (name.size() > sizeof(partition->name)) {
+        return false;
+    }
+    strncpy(partition->name, name.c_str(), sizeof(partition->name));
+    return true;
+}
+
 bool SetBlockReadonly(int fd, bool readonly) {
 #if defined(__linux__)
     int val = readonly;
@@ -207,5 +219,70 @@
     return base::unique_fd(open(path, flags));
 }
 
+bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
+                                      uint32_t target_slot_number) {
+    std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
+    std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
+
+    // There can be leftover groups with target suffix on retrofit devices.
+    // They are useless now, so delete.
+    std::vector<LpMetadataPartitionGroup*> new_group_ptrs;
+    for (auto& group : metadata->groups) {
+        std::string group_name = GetPartitionGroupName(group);
+        std::string slot_suffix = GetPartitionSlotSuffix(group_name);
+        // Don't add groups with target slot suffix.
+        if (slot_suffix == target_slot_suffix) continue;
+        // Replace source slot suffix with target slot suffix.
+        if (slot_suffix == source_slot_suffix) {
+            std::string new_name = group_name.substr(0, group_name.size() - slot_suffix.size()) +
+                                   target_slot_suffix;
+            if (!UpdatePartitionGroupName(&group, new_name)) {
+                LERROR << "Group name too long: " << new_name;
+                return false;
+            }
+        }
+        new_group_ptrs.push_back(&group);
+    }
+
+    std::vector<LpMetadataPartition*> new_partition_ptrs;
+    for (auto& partition : metadata->partitions) {
+        std::string partition_name = GetPartitionName(partition);
+        std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+        // Don't add partitions with target slot suffix.
+        if (slot_suffix == target_slot_suffix) continue;
+        // Replace source slot suffix with target slot suffix.
+        if (slot_suffix == source_slot_suffix) {
+            std::string new_name =
+                    partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
+                    target_slot_suffix;
+            if (!UpdatePartitionName(&partition, new_name)) {
+                LERROR << "Partition name too long: " << new_name;
+                return false;
+            }
+        }
+        // Update group index.
+        auto it = std::find(new_group_ptrs.begin(), new_group_ptrs.end(),
+                            &metadata->groups[partition.group_index]);
+        if (it == new_group_ptrs.end()) {
+            LWARN << "Removing partition " << partition_name << " from group "
+                  << GetPartitionGroupName(metadata->groups[partition.group_index])
+                  << "; this partition should not belong to this group!";
+            continue;  // not adding to new_partition_ptrs
+        }
+        partition.group_index = std::distance(new_group_ptrs.begin(), it);
+        new_partition_ptrs.push_back(&partition);
+    }
+
+    std::vector<LpMetadataPartition> new_partitions;
+    for (auto* p : new_partition_ptrs) new_partitions.emplace_back(std::move(*p));
+    metadata->partitions = std::move(new_partitions);
+
+    std::vector<LpMetadataPartitionGroup> new_groups;
+    for (auto* g : new_group_ptrs) new_groups.emplace_back(std::move(*g));
+    metadata->groups = std::move(new_groups);
+
+    return true;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 96f1717..25ab66b 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -89,12 +89,17 @@
 // Update names from C++ strings.
 bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
 bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
+bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name);
 
 // Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
 bool SetBlockReadonly(int fd, bool readonly);
 
 ::android::base::unique_fd GetControlFileOrOpen(const char* path, int flags);
 
+// For Virtual A/B updates, modify |metadata| so that it can be written to |target_slot_number|.
+bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,
+                                      uint32_t target_slot_number);
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp
index 15f7fff..cac3989 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <gtest/gtest.h>
+#include <liblp/builder.h>
 #include <liblp/liblp.h>
 
 #include "utility.h"
@@ -75,3 +76,81 @@
     EXPECT_EQ(GetPartitionSlotSuffix("system_a"), "_a");
     EXPECT_EQ(GetPartitionSlotSuffix("system_b"), "_b");
 }
+
+namespace android {
+namespace fs_mgr {
+// Equality comparison for testing. In reality, equality of device_index doesn't
+// necessary mean equality of the block device.
+bool operator==(const LinearExtent& l, const LinearExtent& r) {
+    return l.device_index() == r.device_index() && l.physical_sector() == r.physical_sector() &&
+           l.end_sector() == r.end_sector();
+}
+}  // namespace fs_mgr
+}  // namespace android
+
+static std::vector<LinearExtent> GetPartitionExtents(Partition* p) {
+    std::vector<LinearExtent> extents;
+    for (auto&& extent : p->extents()) {
+        auto linear_extent = extent->AsLinearExtent();
+        if (!linear_extent) return {};
+        extents.push_back(*linear_extent);
+    }
+    return extents;
+}
+
+TEST(liblp, UpdateMetadataForInPlaceSnapshot) {
+    using std::unique_ptr;
+
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    ASSERT_TRUE(builder->AddGroup("group_a", 256 * 1024));
+    Partition* system_a = builder->AddPartition("system_a", "group_a", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system_a, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(system_a, 40 * 1024));
+    Partition* vendor_a = builder->AddPartition("vendor_a", "group_a", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(vendor_a, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(vendor_a, 20 * 1024));
+
+    ASSERT_TRUE(builder->AddGroup("group_b", 258 * 1024));
+    Partition* system_b = builder->AddPartition("system_b", "group_b", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(system_b, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(system_b, 36 * 1024));
+    Partition* vendor_b = builder->AddPartition("vendor_b", "group_b", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(vendor_b, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(vendor_b, 32 * 1024));
+
+    auto system_a_extents = GetPartitionExtents(system_a);
+    ASSERT_FALSE(system_a_extents.empty());
+
+    auto vendor_a_extents = GetPartitionExtents(vendor_a);
+    ASSERT_FALSE(vendor_a_extents.empty());
+
+    auto metadata = builder->Export();
+    ASSERT_NE(nullptr, metadata);
+
+    ASSERT_TRUE(UpdateMetadataForInPlaceSnapshot(metadata.get(), 0, 1));
+
+    auto new_builder = MetadataBuilder::New(*metadata);
+    ASSERT_NE(nullptr, new_builder);
+
+    EXPECT_EQ(nullptr, new_builder->FindGroup("group_a"));
+    EXPECT_EQ(nullptr, new_builder->FindPartition("system_a"));
+    EXPECT_EQ(nullptr, new_builder->FindPartition("vendor_a"));
+
+    auto group_b = new_builder->FindGroup("group_b");
+    ASSERT_NE(nullptr, group_b);
+    ASSERT_EQ(256 * 1024, group_b->maximum_size());
+
+    auto new_system_b = new_builder->FindPartition("system_b");
+    ASSERT_NE(nullptr, new_system_b);
+    EXPECT_EQ(40 * 1024, new_system_b->size());
+    auto new_system_b_extents = GetPartitionExtents(new_system_b);
+    EXPECT_EQ(system_a_extents, new_system_b_extents);
+
+    auto new_vendor_b = new_builder->FindPartition("vendor_b");
+    ASSERT_NE(nullptr, new_vendor_b);
+    EXPECT_EQ(20 * 1024, new_vendor_b->size());
+    auto new_vendor_b_extents = GetPartitionExtents(new_vendor_b);
+    EXPECT_EQ(vendor_a_extents, new_vendor_b_extents);
+}
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index bffcb7e..8a983ad 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -17,6 +17,7 @@
 #include "writer.h"
 
 #include <inttypes.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <string>
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
new file mode 100644
index 0000000..b320db8
--- /dev/null
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -0,0 +1,92 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_defaults {
+    name: "libsnapshot_defaults",
+    defaults: ["fs_mgr_defaults"],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libdm",
+        "libfs_mgr",
+        "liblp",
+    ],
+    whole_static_libs: [
+        "libext2_uuid",
+        "libext4_utils",
+        "libfstab",
+    ],
+    header_libs: [
+        "libfiemap_headers",
+    ],
+    export_header_lib_headers: [
+        "libfiemap_headers",
+    ],
+    export_include_dirs: ["include"],
+}
+
+filegroup {
+    name: "libsnapshot_sources",
+    srcs: [
+        "snapshot.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "libsnapshot",
+    defaults: ["libsnapshot_defaults"],
+    srcs: [":libsnapshot_sources"],
+    whole_static_libs: [
+        "libfiemap_binder",
+    ],
+}
+
+cc_library_static {
+    name: "libsnapshot_nobinder",
+    defaults: ["libsnapshot_defaults"],
+    srcs: [":libsnapshot_sources"],
+    recovery_available: true,
+    whole_static_libs: [
+        "libfiemap_passthrough",
+    ],
+}
+
+cc_test {
+    name: "libsnapshot_test",
+    defaults: ["libsnapshot_defaults"],
+    srcs: [
+        "snapshot_test.cpp",
+        "test_helpers.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+    ],
+    static_libs: [
+        "libcutils",
+        "libcrypto",
+        "libfs_mgr",
+        "liblp",
+        "libsnapshot",
+    ],
+}
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
new file mode 100644
index 0000000..0cfa7e4
--- /dev/null
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -0,0 +1,2 @@
+dvander@google.com
+elsk@google.com
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
new file mode 100644
index 0000000..0c0355d
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -0,0 +1,323 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <chrono>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
+
+#ifndef FRIEND_TEST
+#define FRIEND_TEST(test_set_name, individual_test) \
+    friend class test_set_name##_##individual_test##_Test
+#define DEFINED_FRIEND_TEST
+#endif
+
+namespace android {
+
+namespace fiemap {
+class IImageManager;
+}  // namespace fiemap
+
+namespace fs_mgr {
+struct CreateLogicalPartitionParams;
+class IPartitionOpener;
+}  // namespace fs_mgr
+
+namespace snapshot {
+
+enum class UpdateState : unsigned int {
+    // No update or merge is in progress.
+    None,
+
+    // An update is applying; snapshots may already exist.
+    Initiated,
+
+    // An update is pending, but has not been successfully booted yet.
+    Unverified,
+
+    // The kernel is merging in the background.
+    Merging,
+
+    // Post-merge cleanup steps could not be completed due to a transient
+    // error, but the next reboot will finish any pending operations.
+    MergeNeedsReboot,
+
+    // Merging is complete, and needs to be acknowledged.
+    MergeCompleted,
+
+    // Merging failed due to an unrecoverable error.
+    MergeFailed
+};
+
+class SnapshotManager final {
+    using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
+    using LpMetadata = android::fs_mgr::LpMetadata;
+    using IPartitionOpener = android::fs_mgr::IPartitionOpener;
+
+  public:
+    // Dependency injection for testing.
+    class IDeviceInfo {
+      public:
+        virtual ~IDeviceInfo() {}
+        virtual std::string GetGsidDir() const = 0;
+        virtual std::string GetMetadataDir() const = 0;
+        virtual std::string GetSlotSuffix() const = 0;
+        virtual const IPartitionOpener& GetPartitionOpener() const = 0;
+    };
+
+    ~SnapshotManager();
+
+    // Return a new SnapshotManager instance, or null on error. The device
+    // pointer is owned for the lifetime of SnapshotManager. If null, a default
+    // instance will be created.
+    static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);
+
+    // This is similar to New(), except designed specifically for first-stage
+    // init.
+    static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);
+
+    // Helper function for first-stage init to check whether a SnapshotManager
+    // might be needed to perform first-stage mounts.
+    static bool IsSnapshotManagerNeeded();
+
+    // Begin an update. This must be called before creating any snapshots. It
+    // will fail if GetUpdateState() != None.
+    bool BeginUpdate();
+
+    // Cancel an update; any snapshots will be deleted. This will fail if the
+    // state != Initiated or None.
+    bool CancelUpdate();
+
+    // Mark snapshot writes as having completed. After this, new snapshots cannot
+    // be created, and the device must either cancel the OTA (either before
+    // rebooting or after rolling back), or merge the OTA.
+    bool FinishedSnapshotWrites();
+
+    // Initiate a merge on all snapshot devices. This should only be used after an
+    // update has been marked successful after booting.
+    bool InitiateMerge();
+
+    // Wait for the current merge to finish, then perform cleanup when it
+    // completes. It is necessary to call this after InitiateMerge(), or when
+    // a merge state is detected during boot.
+    //
+    // Note that after calling WaitForMerge(), GetUpdateState() may still return
+    // that a merge is in progress:
+    //   MergeFailed indicates that a fatal error occurred. WaitForMerge() may
+    //   called any number of times again to attempt to make more progress, but
+    //   we do not expect it to succeed if a catastrophic error occurred.
+    //
+    //   MergeNeedsReboot indicates that the merge has completed, but cleanup
+    //   failed. This can happen if for some reason resources were not closed
+    //   properly. In this case another reboot is needed before we can take
+    //   another OTA. However, WaitForMerge() can be called again without
+    //   rebooting, to attempt to finish cleanup anyway.
+    //
+    //   MergeCompleted indicates that the update has fully completed.
+    //   GetUpdateState will return None, and a new update can begin.
+    UpdateState WaitForMerge();
+
+    // Find the status of the current update, if any.
+    //
+    // |progress| depends on the returned status:
+    //   Merging: Value in the range [0, 100]
+    //   MergeCompleted: 100
+    //   Other: 0
+    UpdateState GetUpdateState(double* progress = nullptr);
+
+    // If this returns true, first-stage mount must call
+    // CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.
+    bool NeedSnapshotsInFirstStageMount();
+
+    // Perform first-stage mapping of snapshot targets. This replaces init's
+    // call to CreateLogicalPartitions when snapshots are present.
+    bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
+
+  private:
+    FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
+    FRIEND_TEST(SnapshotTest, CreateSnapshot);
+    FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);
+    FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);
+    FRIEND_TEST(SnapshotTest, MapPartialSnapshot);
+    FRIEND_TEST(SnapshotTest, MapSnapshot);
+    FRIEND_TEST(SnapshotTest, Merge);
+    FRIEND_TEST(SnapshotTest, MergeCannotRemoveCow);
+    FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
+    friend class SnapshotTest;
+
+    using DmTargetSnapshot = android::dm::DmTargetSnapshot;
+    using IImageManager = android::fiemap::IImageManager;
+    using TargetInfo = android::dm::DeviceMapper::TargetInfo;
+
+    explicit SnapshotManager(IDeviceInfo* info);
+
+    // This is created lazily since it can connect via binder.
+    bool EnsureImageManager();
+
+    // Helper for first-stage init.
+    bool ForceLocalImageManager();
+
+    // Helper function for tests.
+    IImageManager* image_manager() const { return images_.get(); }
+
+    // Since libsnapshot is included into multiple processes, we flock() our
+    // files for simple synchronization. LockedFile is a helper to assist with
+    // this. It also serves as a proof-of-lock for some functions.
+    class LockedFile final {
+      public:
+        LockedFile(const std::string& path, android::base::unique_fd&& fd, int lock_mode)
+            : path_(path), fd_(std::move(fd)), lock_mode_(lock_mode) {}
+        ~LockedFile();
+
+        const std::string& path() const { return path_; }
+        int fd() const { return fd_; }
+        int lock_mode() const { return lock_mode_; }
+
+      private:
+        std::string path_;
+        android::base::unique_fd fd_;
+        int lock_mode_;
+    };
+    std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
+    bool Truncate(LockedFile* file);
+
+    // Create a new snapshot record. This creates the backing COW store and
+    // persists information needed to map the device. The device can be mapped
+    // with MapSnapshot().
+    //
+    // |device_size| should be the size of the base_device that will be passed
+    // via MapDevice(). |snapshot_size| should be the number of bytes in the
+    // base device, starting from 0, that will be snapshotted. The cow_size
+    // should be the amount of space that will be allocated to store snapshot
+    // deltas.
+    //
+    // If |snapshot_size| < device_size, then the device will always
+    // be mapped with two table entries: a dm-snapshot range covering
+    // snapshot_size, and a dm-linear range covering the remainder.
+    //
+    // All sizes are specified in bytes, and the device and snapshot sizes
+    // must be a multiple of the sector size (512 bytes). |cow_size| will
+    // be rounded up to the nearest sector.
+    bool CreateSnapshot(LockedFile* lock, const std::string& name, uint64_t device_size,
+                        uint64_t snapshot_size, uint64_t cow_size);
+
+    // Map a snapshot device that was previously created with CreateSnapshot.
+    // If a merge was previously initiated, the device-mapper table will have a
+    // snapshot-merge target instead of a snapshot target. If the timeout
+    // parameter greater than zero, this function will wait the given amount
+    // of time for |dev_path| to become available, and fail otherwise. If
+    // timeout_ms is 0, then no wait will occur and |dev_path| may not yet
+    // exist on return.
+    bool MapSnapshot(LockedFile* lock, const std::string& name, const std::string& base_device,
+                     const std::chrono::milliseconds& timeout_ms, std::string* dev_path);
+
+    // Remove the backing copy-on-write image for the named snapshot. The
+    // caller is responsible for ensuring that the snapshot is unmapped.
+    bool DeleteSnapshot(LockedFile* lock, const std::string& name);
+
+    // Unmap a snapshot device previously mapped with MapSnapshotDevice().
+    bool UnmapSnapshot(LockedFile* lock, const std::string& name);
+
+    // Unmap and remove all known snapshots.
+    bool RemoveAllSnapshots(LockedFile* lock);
+
+    // List the known snapshot names.
+    bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots);
+
+    // Interact with /metadata/ota/state.
+    std::unique_ptr<LockedFile> OpenStateFile(int open_flags, int lock_flags);
+    std::unique_ptr<LockedFile> LockShared();
+    std::unique_ptr<LockedFile> LockExclusive();
+    UpdateState ReadUpdateState(LockedFile* file);
+    bool WriteUpdateState(LockedFile* file, UpdateState state);
+    std::string GetStateFilePath() const;
+
+    enum class SnapshotState : int { Created, Merging, MergeCompleted };
+    static std::string to_string(SnapshotState state);
+
+    // This state is persisted per-snapshot in /metadata/ota/snapshots/.
+    struct SnapshotStatus {
+        SnapshotState state;
+        uint64_t device_size;
+        uint64_t snapshot_size;
+        // These are non-zero when merging.
+        uint64_t sectors_allocated = 0;
+        uint64_t metadata_sectors = 0;
+    };
+
+    // Helpers for merging.
+    bool SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);
+    bool RewriteSnapshotDeviceTable(const std::string& dm_name);
+    bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);
+    void AcknowledgeMergeSuccess(LockedFile* lock);
+    void AcknowledgeMergeFailure();
+
+    // Note that these require the name of the device containing the snapshot,
+    // which may be the "inner" device. Use GetsnapshotDeviecName().
+    bool QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,
+                             DmTargetSnapshot::Status* status);
+    bool IsSnapshotDevice(const std::string& dm_name, TargetInfo* target = nullptr);
+
+    // Internal callback for when merging is complete.
+    bool OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
+                                 const SnapshotStatus& status);
+    bool CollapseSnapshotDevice(const std::string& name, const SnapshotStatus& status);
+
+    // Only the following UpdateStates are used here:
+    //   UpdateState::Merging
+    //   UpdateState::MergeCompleted
+    //   UpdateState::MergeFailed
+    //   UpdateState::MergeNeedsReboot
+    UpdateState CheckMergeState();
+    UpdateState CheckMergeState(LockedFile* lock);
+    UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
+
+    // Interact with status files under /metadata/ota/snapshots.
+    bool WriteSnapshotStatus(LockedFile* lock, const std::string& name,
+                             const SnapshotStatus& status);
+    bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
+    std::string GetSnapshotStatusFilePath(const std::string& name);
+
+    std::string GetSnapshotBootIndicatorPath();
+    void RemoveSnapshotBootIndicator();
+
+    // Return the name of the device holding the "snapshot" or "snapshot-merge"
+    // target. This may not be the final device presented via MapSnapshot(), if
+    // for example there is a linear segment.
+    std::string GetSnapshotDeviceName(const std::string& snapshot_name,
+                                      const SnapshotStatus& status);
+
+    std::string gsid_dir_;
+    std::string metadata_dir_;
+    std::unique_ptr<IDeviceInfo> device_;
+    std::unique_ptr<IImageManager> images_;
+    bool has_local_image_manager_ = false;
+};
+
+}  // namespace snapshot
+}  // namespace android
+
+#ifdef DEFINED_FRIEND_TEST
+#undef DEFINED_FRIEND_TEST
+#undef FRIEND_TEST
+#endif
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
new file mode 100644
index 0000000..c975c03
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -0,0 +1,1339 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <libsnapshot/snapshot.h>
+
+#include <dirent.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/unistd.h>
+
+#include <thread>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr_dm_linear.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+#include <liblp/liblp.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::dm::DmTable;
+using android::dm::DmTargetLinear;
+using android::dm::DmTargetSnapshot;
+using android::dm::kSectorSize;
+using android::dm::SnapshotStorageMode;
+using android::fiemap::IImageManager;
+using android::fs_mgr::CreateLogicalPartition;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::GetPartitionName;
+using android::fs_mgr::LpMetadata;
+using android::fs_mgr::SlotNumberForSlotSuffix;
+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 {
+  public:
+    std::string GetGsidDir() const override { return "ota"s; }
+    std::string GetMetadataDir() const override { return "/metadata/ota"s; }
+    std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
+    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const { return opener_; }
+
+  private:
+    android::fs_mgr::PartitionOpener opener_;
+};
+
+// Note: IImageManager is an incomplete type in the header, so the default
+// destructor doesn't work.
+SnapshotManager::~SnapshotManager() {}
+
+std::unique_ptr<SnapshotManager> SnapshotManager::New(IDeviceInfo* info) {
+    if (!info) {
+        info = new DeviceInfo();
+    }
+    return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
+}
+
+std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
+    auto sm = New(info);
+    if (!sm || !sm->ForceLocalImageManager()) {
+        return nullptr;
+    }
+    return sm;
+}
+
+SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
+    gsid_dir_ = device_->GetGsidDir();
+    metadata_dir_ = device_->GetMetadataDir();
+}
+
+static std::string GetCowName(const std::string& snapshot_name) {
+    return snapshot_name + "-cow";
+}
+
+static std::string GetBaseDeviceName(const std::string& partition_name) {
+    return partition_name + "-base";
+}
+
+bool SnapshotManager::BeginUpdate() {
+    auto file = LockExclusive();
+    if (!file) return false;
+
+    auto state = ReadUpdateState(file.get());
+    if (state != UpdateState::None) {
+        LOG(ERROR) << "An update is already in progress, cannot begin a new update";
+        return false;
+    }
+    return WriteUpdateState(file.get(), UpdateState::Initiated);
+}
+
+bool SnapshotManager::CancelUpdate() {
+    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 (!RemoveAllSnapshots(file.get())) {
+        LOG(ERROR) << "Could not remove all snapshots";
+        return false;
+    }
+
+    if (!WriteUpdateState(file.get(), UpdateState::None)) {
+        LOG(ERROR) << "Could not write new update state";
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::FinishedSnapshotWrites() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    if (ReadUpdateState(lock.get()) != UpdateState::Initiated) {
+        LOG(ERROR) << "Can only transition to the Unverified state from the Initiated state.";
+        return false;
+    }
+
+    // This file acts as both a quick indicator for init (it can use access(2)
+    // to decide how to do first-stage mounts), and it stores the old slot, so
+    // we can tell whether or not we performed a rollback.
+    auto contents = device_->GetSlotSuffix();
+    auto boot_file = GetSnapshotBootIndicatorPath();
+    if (!android::base::WriteStringToFile(contents, boot_file)) {
+        PLOG(ERROR) << "write failed: " << boot_file;
+        return false;
+    }
+    return WriteUpdateState(lock.get(), UpdateState::Unverified);
+}
+
+bool SnapshotManager::CreateSnapshot(LockedFile* lock, const std::string& name,
+                                     uint64_t device_size, uint64_t snapshot_size,
+                                     uint64_t cow_size) {
+    CHECK(lock);
+    if (!EnsureImageManager()) 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,
+    // can be arbitrarily larger than specified, so we can safely round it up.
+    if (device_size % kSectorSize != 0) {
+        LOG(ERROR) << "Snapshot " << name
+                   << " device size is not a multiple of the sector size: " << device_size;
+        return false;
+    }
+    if (snapshot_size % kSectorSize != 0) {
+        LOG(ERROR) << "Snapshot " << name
+                   << " snapshot size is not a multiple of the sector size: " << snapshot_size;
+        return false;
+    }
+
+    // Round the COW size up to the nearest sector.
+    cow_size += kSectorSize - 1;
+    cow_size &= ~(kSectorSize - 1);
+
+    LOG(INFO) << "Snapshot " << name << " will have COW size " << cow_size;
+
+    // Note, we leave the status file hanging around if we fail to create the
+    // actual backing image. This is harmless, since it'll get removed when
+    // CancelUpdate is called.
+    SnapshotStatus status = {
+            .state = SnapshotState::Created,
+            .device_size = device_size,
+            .snapshot_size = snapshot_size,
+    };
+    if (!WriteSnapshotStatus(lock, name, status)) {
+        PLOG(ERROR) << "Could not write snapshot status: " << name;
+        return false;
+    }
+
+    auto cow_name = GetCowName(name);
+    int cow_flags = IImageManager::CREATE_IMAGE_ZERO_FILL;
+    return images_->CreateBackingImage(cow_name, cow_size, cow_flags);
+}
+
+bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
+                                  const std::string& base_device,
+                                  const std::chrono::milliseconds& timeout_ms,
+                                  std::string* dev_path) {
+    CHECK(lock);
+    if (!EnsureImageManager()) return false;
+
+    SnapshotStatus status;
+    if (!ReadSnapshotStatus(lock, name, &status)) {
+        return false;
+    }
+    if (status.state == SnapshotState::MergeCompleted) {
+        LOG(ERROR) << "Should not create a snapshot device for " << name
+                   << " after merging has completed.";
+        return false;
+    }
+
+    // Validate the block device size, as well as the requested snapshot size.
+    // Note that during first-stage init, we don't have the device paths.
+    if (android::base::StartsWith(base_device, "/")) {
+        unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
+        if (fd < 0) {
+            PLOG(ERROR) << "open failed: " << base_device;
+            return false;
+        }
+        auto dev_size = get_block_device_size(fd);
+        if (!dev_size) {
+            PLOG(ERROR) << "Could not determine block device size: " << base_device;
+            return false;
+        }
+        if (status.device_size != dev_size) {
+            LOG(ERROR) << "Block device size for " << base_device << " does not match"
+                       << "(expected " << status.device_size << ", got " << dev_size << ")";
+            return false;
+        }
+    }
+    if (status.device_size % kSectorSize != 0) {
+        LOG(ERROR) << "invalid blockdev size for " << base_device << ": " << status.device_size;
+        return false;
+    }
+    if (status.snapshot_size % kSectorSize != 0 || status.snapshot_size > status.device_size) {
+        LOG(ERROR) << "Invalid snapshot size for " << base_device << ": " << status.snapshot_size;
+        return false;
+    }
+    uint64_t snapshot_sectors = status.snapshot_size / kSectorSize;
+    uint64_t linear_sectors = (status.device_size - status.snapshot_size) / kSectorSize;
+
+    auto cow_name = GetCowName(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_name, &cow_dev);
+    } else {
+        ok = images_->MapImageDevice(cow_name, timeout_ms, &cow_dev);
+    }
+    if (!ok) {
+        LOG(ERROR) << "Could not map image device: " << cow_name;
+        return false;
+    }
+
+    auto& dm = DeviceMapper::Instance();
+
+    // Note that merging is a global state. We do track whether individual devices
+    // have completed merging, but the start of the merge process is considered
+    // atomic.
+    SnapshotStorageMode mode;
+    switch (ReadUpdateState(lock)) {
+        case UpdateState::MergeCompleted:
+        case UpdateState::MergeNeedsReboot:
+            LOG(ERROR) << "Should not create a snapshot device for " << name
+                       << " after global merging has completed.";
+            return false;
+        case UpdateState::Merging:
+        case UpdateState::MergeFailed:
+            // Note: MergeFailed indicates that a merge is in progress, but
+            // is possibly stalled. We still have to honor the merge.
+            mode = SnapshotStorageMode::Merge;
+            break;
+        default:
+            mode = SnapshotStorageMode::Persistent;
+            break;
+    }
+
+    // The kernel (tested on 4.19) crashes horribly if a device has both a snapshot
+    // and a linear target in the same table. Instead, we stack them, and give the
+    // snapshot device a different name. It is not exposed to the caller in this
+    // case.
+    auto snap_name = (linear_sectors > 0) ? name + "-inner" : name;
+
+    DmTable table;
+    table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_dev, mode,
+                                    kSnapshotChunkSize);
+    if (!dm.CreateDevice(snap_name, table, dev_path, timeout_ms)) {
+        LOG(ERROR) << "Could not create snapshot device: " << snap_name;
+        images_->UnmapImageDevice(cow_name);
+        return false;
+    }
+
+    if (linear_sectors) {
+        // 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>(snapshot_sectors, linear_sectors, base_device,
+                                      snapshot_sectors);
+        if (!dm.CreateDevice(name, table, dev_path, timeout_ms)) {
+            LOG(ERROR) << "Could not create outer snapshot device: " << name;
+            dm.DeleteDevice(snap_name);
+            images_->UnmapImageDevice(cow_name);
+            return false;
+        }
+    }
+
+    // :TODO: when merging is implemented, we need to add an argument to the
+    // status indicating how much progress is left to merge. (device-mapper
+    // does not retain the initial values, so we can't derive them.)
+    return true;
+}
+
+bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
+    CHECK(lock);
+    if (!EnsureImageManager()) return false;
+
+    SnapshotStatus status;
+    if (!ReadSnapshotStatus(lock, name, &status)) {
+        return false;
+    }
+
+    auto& dm = DeviceMapper::Instance();
+    if (dm.GetState(name) != DmDeviceState::INVALID && !dm.DeleteDevice(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.GetState(dm_name) != DmDeviceState::INVALID &&
+        !dm.DeleteDevice(dm_name)) {
+        LOG(ERROR) << "Could not delete inner snapshot device: " << dm_name;
+        return false;
+    }
+
+    auto cow_name = GetCowName(name);
+    if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::DeleteSnapshot(LockedFile* lock, const std::string& name) {
+    CHECK(lock);
+    if (!EnsureImageManager()) return false;
+
+    auto cow_name = GetCowName(name);
+    if (!images_->BackingImageExists(cow_name)) {
+        return true;
+    }
+    if (images_->IsImageMapped(cow_name) && !images_->UnmapImageDevice(cow_name)) {
+        return false;
+    }
+    if (!images_->DeleteBackingImage(cow_name)) {
+        return false;
+    }
+
+    std::string error;
+    auto file_path = GetSnapshotStatusFilePath(name);
+    if (!android::base::RemoveFileIfExists(file_path, &error)) {
+        LOG(ERROR) << "Failed to remove status file " << file_path << ": " << error;
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::InitiateMerge() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    UpdateState state = ReadUpdateState(lock.get());
+    if (state != UpdateState::Unverified) {
+        LOG(ERROR) << "Cannot begin a merge if an update has not been verified";
+        return false;
+    }
+
+    std::string old_slot;
+    auto boot_file = GetSnapshotBootIndicatorPath();
+    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
+        LOG(ERROR) << "Could not determine the previous slot; aborting merge";
+        return false;
+    }
+    auto new_slot = device_->GetSlotSuffix();
+    if (new_slot == old_slot) {
+        LOG(ERROR) << "Device cannot merge while booting off old slot " << old_slot;
+        return false;
+    }
+
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots)) {
+        LOG(ERROR) << "Could not list snapshots";
+        return false;
+    }
+
+    auto& dm = DeviceMapper::Instance();
+    for (const auto& snapshot : snapshots) {
+        // The device has to be mapped, since everything should be merged at
+        // the same time. This is a fairly serious error. We could forcefully
+        // map everything here, but it should have been mapped during first-
+        // stage init.
+        if (dm.GetState(snapshot) == DmDeviceState::INVALID) {
+            LOG(ERROR) << "Cannot begin merge; device " << snapshot << " is not mapped.";
+            return false;
+        }
+    }
+
+    // Point of no return - mark that we're starting a merge. From now on every
+    // snapshot must be a merge target.
+    if (!WriteUpdateState(lock.get(), UpdateState::Merging)) {
+        return false;
+    }
+
+    bool rewrote_all = true;
+    for (const auto& snapshot : snapshots) {
+        // If this fails, we have no choice but to continue. Everything must
+        // be merged. This is not an ideal state to be in, but it is safe,
+        // because we the next boot will try again.
+        if (!SwitchSnapshotToMerge(lock.get(), snapshot)) {
+            LOG(ERROR) << "Failed to switch snapshot to a merge target: " << snapshot;
+            rewrote_all = false;
+        }
+    }
+
+    // If we couldn't switch everything to a merge target, pre-emptively mark
+    // this merge as failed. It will get acknowledged when WaitForMerge() is
+    // called.
+    if (!rewrote_all) {
+        WriteUpdateState(lock.get(), UpdateState::MergeFailed);
+    }
+
+    // Return true no matter what, because a merge was initiated.
+    return true;
+}
+
+bool SnapshotManager::SwitchSnapshotToMerge(LockedFile* lock, const std::string& name) {
+    SnapshotStatus status;
+    if (!ReadSnapshotStatus(lock, name, &status)) {
+        return false;
+    }
+    if (status.state != SnapshotState::Created) {
+        LOG(WARNING) << "Snapshot " << name << " has unexpected state: " << to_string(status.state);
+    }
+
+    // After this, we return true because we technically did switch to a merge
+    // target. Everything else we do here is just informational.
+    auto dm_name = GetSnapshotDeviceName(name, status);
+    if (!RewriteSnapshotDeviceTable(dm_name)) {
+        return false;
+    }
+
+    status.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)) {
+        LOG(ERROR) << "Could not update status file for snapshot: " << name;
+    }
+    return true;
+}
+
+bool SnapshotManager::RewriteSnapshotDeviceTable(const std::string& dm_name) {
+    auto& dm = DeviceMapper::Instance();
+
+    std::vector<DeviceMapper::TargetInfo> old_targets;
+    if (!dm.GetTableInfo(dm_name, &old_targets)) {
+        LOG(ERROR) << "Could not read snapshot device table: " << dm_name;
+        return false;
+    }
+    if (old_targets.size() != 1 || DeviceMapper::GetTargetType(old_targets[0].spec) != "snapshot") {
+        LOG(ERROR) << "Unexpected device-mapper table for snapshot: " << dm_name;
+        return false;
+    }
+
+    std::string base_device, cow_device;
+    if (!DmTargetSnapshot::GetDevicesFromParams(old_targets[0].data, &base_device, &cow_device)) {
+        LOG(ERROR) << "Could not derive underlying devices for snapshot: " << dm_name;
+        return false;
+    }
+
+    DmTable table;
+    table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,
+                                    SnapshotStorageMode::Merge, kSnapshotChunkSize);
+    if (!dm.LoadTableAndActivate(dm_name, table)) {
+        LOG(ERROR) << "Could not swap device-mapper tables on snapshot device " << dm_name;
+        return false;
+    }
+    LOG(INFO) << "Successfully switched snapshot device to a merge target: " << dm_name;
+    return true;
+}
+
+enum class TableQuery {
+    Table,
+    Status,
+};
+
+static bool GetSingleTarget(const std::string& dm_name, TableQuery query,
+                            DeviceMapper::TargetInfo* target) {
+    auto& dm = DeviceMapper::Instance();
+    if (dm.GetState(dm_name) == DmDeviceState::INVALID) {
+        return false;
+    }
+
+    std::vector<DeviceMapper::TargetInfo> targets;
+    bool result;
+    if (query == TableQuery::Status) {
+        result = dm.GetTableStatus(dm_name, &targets);
+    } else {
+        result = dm.GetTableInfo(dm_name, &targets);
+    }
+    if (!result) {
+        LOG(ERROR) << "Could not query device: " << dm_name;
+        return false;
+    }
+    if (targets.size() != 1) {
+        return false;
+    }
+
+    *target = std::move(targets[0]);
+    return true;
+}
+
+bool SnapshotManager::IsSnapshotDevice(const std::string& dm_name, TargetInfo* target) {
+    DeviceMapper::TargetInfo snap_target;
+    if (!GetSingleTarget(dm_name, TableQuery::Status, &snap_target)) {
+        return false;
+    }
+    auto type = DeviceMapper::GetTargetType(snap_target.spec);
+    if (type != "snapshot" && type != "snapshot-merge") {
+        return false;
+    }
+    if (target) {
+        *target = std::move(snap_target);
+    }
+    return true;
+}
+
+bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,
+                                          DmTargetSnapshot::Status* status) {
+    DeviceMapper::TargetInfo target;
+    if (!IsSnapshotDevice(dm_name, &target)) {
+        LOG(ERROR) << "Device " << dm_name << " is not a snapshot or snapshot-merge device";
+        return false;
+    }
+    if (!DmTargetSnapshot::ParseStatusText(target.data, status)) {
+        LOG(ERROR) << "Could not parse snapshot status text: " << dm_name;
+        return false;
+    }
+    if (target_type) {
+        *target_type = DeviceMapper::GetTargetType(target.spec);
+    }
+    return true;
+}
+
+// Note that when a merge fails, we will *always* try again to complete the
+// merge each time the device boots. There is no harm in doing so, and if
+// the problem was transient, we might manage to get a new outcome.
+UpdateState SnapshotManager::WaitForMerge() {
+    while (true) {
+        UpdateState state = CheckMergeState();
+        if (state != UpdateState::Merging) {
+            // Either there is no merge, or the merge was finished, so no need
+            // to keep waiting.
+            return state;
+        }
+
+        // This wait is not super time sensitive, so we have a relatively
+        // low polling frequency.
+        std::this_thread::sleep_for(2s);
+    }
+}
+
+UpdateState SnapshotManager::CheckMergeState() {
+    auto lock = LockExclusive();
+    if (!lock) {
+        AcknowledgeMergeFailure();
+        return UpdateState::MergeFailed;
+    }
+
+    auto state = CheckMergeState(lock.get());
+    if (state == UpdateState::MergeCompleted) {
+        AcknowledgeMergeSuccess(lock.get());
+    } else if (state == UpdateState::MergeFailed) {
+        AcknowledgeMergeFailure();
+    }
+    return state;
+}
+
+UpdateState SnapshotManager::CheckMergeState(LockedFile* lock) {
+    UpdateState state = ReadUpdateState(lock);
+    switch (state) {
+        case UpdateState::None:
+        case UpdateState::MergeCompleted:
+            // Harmless races are allowed between two callers of WaitForMerge,
+            // so in both of these cases we just propagate the state.
+            return state;
+
+        case UpdateState::Merging:
+        case UpdateState::MergeNeedsReboot:
+        case UpdateState::MergeFailed:
+            // We'll poll each snapshot below. Note that for the NeedsReboot
+            // case, we always poll once to give cleanup another opportunity to
+            // run.
+            break;
+
+        default:
+            LOG(ERROR) << "No merge exists, cannot wait. Update state: "
+                       << static_cast<uint32_t>(state);
+            return UpdateState::None;
+    }
+
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock, &snapshots)) {
+        return UpdateState::MergeFailed;
+    }
+
+    bool failed = false;
+    bool merging = false;
+    bool needs_reboot = false;
+    for (const auto& snapshot : snapshots) {
+        UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot);
+        switch (snapshot_state) {
+            case UpdateState::MergeFailed:
+                failed = true;
+                break;
+            case UpdateState::Merging:
+                merging = true;
+                break;
+            case UpdateState::MergeNeedsReboot:
+                needs_reboot = true;
+                break;
+            case UpdateState::MergeCompleted:
+                break;
+            default:
+                LOG(ERROR) << "Unknown merge status: " << static_cast<uint32_t>(snapshot_state);
+                failed = true;
+                break;
+        }
+    }
+
+    if (merging) {
+        // Note that we handle "Merging" before we handle anything else. We
+        // want to poll until *nothing* is merging if we can, so everything has
+        // a chance to get marked as completed or failed.
+        return UpdateState::Merging;
+    }
+    if (failed) {
+        // Note: since there are many drop-out cases for failure, we acknowledge
+        // it in WaitForMerge rather than here and elsewhere.
+        return UpdateState::MergeFailed;
+    }
+    if (needs_reboot) {
+        WriteUpdateState(lock, UpdateState::MergeNeedsReboot);
+        return UpdateState::MergeNeedsReboot;
+    }
+    return UpdateState::MergeCompleted;
+}
+
+UpdateState SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name) {
+    SnapshotStatus snapshot_status;
+    if (!ReadSnapshotStatus(lock, name, &snapshot_status)) {
+        return UpdateState::MergeFailed;
+    }
+
+    std::string dm_name = GetSnapshotDeviceName(name, snapshot_status);
+
+    // During a check, we decided the merge was complete, but we were unable to
+    // collapse the device-mapper stack and perform COW cleanup. If we haven't
+    // rebooted after this check, the device will still be a snapshot-merge
+    // target. If the have rebooted, the device will now be a linear target,
+    // and we can try cleanup again.
+    if (snapshot_status.state == SnapshotState::MergeCompleted && !IsSnapshotDevice(dm_name)) {
+        // NB: It's okay if this fails now, we gave cleanup our best effort.
+        OnSnapshotMergeComplete(lock, name, snapshot_status);
+        return UpdateState::MergeCompleted;
+    }
+
+    std::string target_type;
+    DmTargetSnapshot::Status status;
+    if (!QuerySnapshotStatus(dm_name, &target_type, &status)) {
+        return UpdateState::MergeFailed;
+    }
+    if (target_type != "snapshot-merge") {
+        // We can get here if we failed to rewrite the target type in
+        // InitiateMerge(). If we failed to create the target in first-stage
+        // init, boot would not succeed.
+        LOG(ERROR) << "Snapshot " << name << " has incorrect target type: " << target_type;
+        return UpdateState::MergeFailed;
+    }
+
+    // These two values are equal when merging is complete.
+    if (status.sectors_allocated != status.metadata_sectors) {
+        if (snapshot_status.state == SnapshotState::MergeCompleted) {
+            LOG(ERROR) << "Snapshot " << name << " is merging after being marked merge-complete.";
+            return UpdateState::MergeFailed;
+        }
+        return UpdateState::Merging;
+    }
+
+    // Merging is done. First, update the status file to indicate the merge
+    // is complete. We do this before calling OnSnapshotMergeComplete, even
+    // though this means the write is potentially wasted work (since in the
+    // ideal case we'll immediately delete the file).
+    //
+    // This makes it simpler to reason about the next reboot: no matter what
+    // part of cleanup failed, first-stage init won't try to create another
+    // snapshot device for this partition.
+    snapshot_status.state = SnapshotState::MergeCompleted;
+    if (!WriteSnapshotStatus(lock, name, snapshot_status)) {
+        return UpdateState::MergeFailed;
+    }
+    if (!OnSnapshotMergeComplete(lock, name, snapshot_status)) {
+        return UpdateState::MergeNeedsReboot;
+    }
+    return UpdateState::MergeCompleted;
+}
+
+std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
+    return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
+}
+
+void SnapshotManager::RemoveSnapshotBootIndicator() {
+    // It's okay if this fails - first-stage init performs a deeper check after
+    // reading the indicator file, so it's not a problem if it still exists
+    // after the update completes.
+    auto boot_file = GetSnapshotBootIndicatorPath();
+    if (unlink(boot_file.c_str()) == -1 && errno != ENOENT) {
+        PLOG(ERROR) << "unlink " << boot_file;
+    }
+}
+
+void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
+    RemoveSnapshotBootIndicator();
+
+    if (!WriteUpdateState(lock, UpdateState::None)) {
+        // We'll try again next reboot, ad infinitum.
+        return;
+    }
+}
+
+void SnapshotManager::AcknowledgeMergeFailure() {
+    // Log first, so worst case, we always have a record of why the calls below
+    // were being made.
+    LOG(ERROR) << "Merge could not be completed and will be marked as failed.";
+
+    auto lock = LockExclusive();
+    if (!lock) return;
+
+    // Since we released the lock in between WaitForMerge and here, it's
+    // possible (1) the merge successfully completed or (2) was already
+    // marked as a failure. So make sure to check the state again, and
+    // only mark as a failure if appropriate.
+    UpdateState state = ReadUpdateState(lock.get());
+    if (state != UpdateState::Merging && state != UpdateState::MergeNeedsReboot) {
+        return;
+    }
+
+    WriteUpdateState(lock.get(), UpdateState::MergeFailed);
+}
+
+bool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
+                                              const SnapshotStatus& status) {
+    auto dm_name = GetSnapshotDeviceName(name, status);
+    if (IsSnapshotDevice(dm_name)) {
+        // We are extra-cautious here, to avoid deleting the wrong table.
+        std::string target_type;
+        DmTargetSnapshot::Status dm_status;
+        if (!QuerySnapshotStatus(dm_name, &target_type, &dm_status)) {
+            return false;
+        }
+        if (target_type != "snapshot-merge") {
+            LOG(ERROR) << "Unexpected target type " << target_type
+                       << " for snapshot device: " << dm_name;
+            return false;
+        }
+        if (dm_status.sectors_allocated != dm_status.metadata_sectors) {
+            LOG(ERROR) << "Merge is unexpectedly incomplete for device " << dm_name;
+            return false;
+        }
+        if (!CollapseSnapshotDevice(name, status)) {
+            LOG(ERROR) << "Unable to collapse snapshot: " << name;
+            return false;
+        }
+        // Note that collapsing is implicitly an Unmap, so we don't need to
+        // unmap the snapshot.
+    }
+
+    if (!DeleteSnapshot(lock, name)) {
+        LOG(ERROR) << "Could not delete snapshot: " << name;
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
+                                             const SnapshotStatus& status) {
+    // Ideally, we would complete the following steps to collapse the device:
+    //  (1) Rewrite the snapshot table to be identical to the base device table.
+    //  (2) Rewrite the verity table to use the "snapshot" (now linear) device.
+    //  (3) Delete the base device.
+    //
+    // This should be possible once libsnapshot understands LpMetadata. In the
+    // meantime, we implement a simpler solution: rewriting the snapshot table
+    // to be a single dm-linear segment against the base device. While not as
+    // ideal, it still lets us remove the COW device. We can remove this
+    // implementation once the new method has been tested.
+    auto& dm = DeviceMapper::Instance();
+    auto dm_name = GetSnapshotDeviceName(name, status);
+
+    DeviceMapper::TargetInfo target;
+    if (!GetSingleTarget(dm_name, TableQuery::Table, &target)) {
+        return false;
+    }
+    if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
+        // This should be impossible, it was checked above.
+        LOG(ERROR) << "Snapshot device has invalid target type: " << dm_name;
+        return false;
+    }
+
+    std::string base_device, cow_device;
+    if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {
+        LOG(ERROR) << "Could not parse snapshot device " << dm_name
+                   << " parameters: " << target.data;
+        return false;
+    }
+
+    uint64_t num_sectors = status.snapshot_size / kSectorSize;
+    if (num_sectors * kSectorSize != status.snapshot_size) {
+        LOG(ERROR) << "Snapshot " << name
+                   << " size is not sector aligned: " << status.snapshot_size;
+        return false;
+    }
+
+    if (dm_name != name) {
+        // We've derived the base device, but we actually need to replace the
+        // table of the outermost device. Do a quick verification that this
+        // device looks like we expect it to.
+        std::vector<DeviceMapper::TargetInfo> outer_table;
+        if (!dm.GetTableInfo(name, &outer_table)) {
+            LOG(ERROR) << "Could not validate outer snapshot table: " << name;
+            return false;
+        }
+        if (outer_table.size() != 2) {
+            LOG(ERROR) << "Expected 2 dm-linear targets for tabble " << name
+                       << ", got: " << outer_table.size();
+            return false;
+        }
+        for (const auto& target : outer_table) {
+            auto target_type = DeviceMapper::GetTargetType(target.spec);
+            if (target_type != "linear") {
+                LOG(ERROR) << "Outer snapshot table may only contain linear targets, but " << name
+                           << " has target: " << target_type;
+                return false;
+            }
+        }
+        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;
+            return false;
+        }
+    }
+
+    // Note: we are replacing the OUTER table here, so we do not use dm_name.
+    DmTargetLinear new_target(0, num_sectors, base_device, 0);
+    LOG(INFO) << "Replacing snapshot device " << name
+              << " table with: " << new_target.GetParameterString();
+
+    DmTable table;
+    table.Emplace<DmTargetLinear>(new_target);
+    if (!dm.LoadTableAndActivate(name, table)) {
+        return false;
+    }
+
+    if (dm_name != name) {
+        // Attempt to delete the snapshot device. Nothing should be depending on
+        // the device, and device-mapper should have flushed remaining I/O. We
+        // could in theory replace with dm-zero (or re-use the table above), but
+        // for now it's better to know why this would fail.
+        if (!dm.DeleteDevice(dm_name)) {
+            LOG(ERROR) << "Unable to delete snapshot device " << dm_name << ", COW cannot be "
+                       << "reclaimed until after reboot.";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock, &snapshots)) {
+        LOG(ERROR) << "Could not list snapshots";
+        return false;
+    }
+
+    bool ok = true;
+    for (const auto& name : snapshots) {
+        ok &= DeleteSnapshot(lock, name);
+    }
+    return ok;
+}
+
+UpdateState SnapshotManager::GetUpdateState(double* progress) {
+    // If we've never started an update, the state file won't exist.
+    auto state_file = GetStateFilePath();
+    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
+        return UpdateState::None;
+    }
+
+    auto file = LockShared();
+    if (!file) {
+        return UpdateState::None;
+    }
+
+    auto state = ReadUpdateState(file.get());
+    if (progress) {
+        *progress = 0.0;
+        if (state == UpdateState::Merging) {
+            // :TODO: When merging is implemented, set progress_val.
+        } else if (state == UpdateState::MergeCompleted) {
+            *progress = 100.0;
+        }
+    }
+    return state;
+}
+
+bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots) {
+    CHECK(lock);
+
+    auto dir_path = metadata_dir_ + "/snapshots"s;
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dir_path.c_str()), closedir);
+    if (!dir) {
+        PLOG(ERROR) << "opendir failed: " << dir_path;
+        return false;
+    }
+
+    struct dirent* dp;
+    while ((dp = readdir(dir.get())) != nullptr) {
+        if (dp->d_type != DT_REG) continue;
+        snapshots->emplace_back(dp->d_name);
+    }
+    return true;
+}
+
+bool SnapshotManager::IsSnapshotManagerNeeded() {
+    return access(kBootIndicatorPath, F_OK) == 0;
+}
+
+bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
+    // If we fail to read, we'll wind up using CreateLogicalPartitions, which
+    // will create devices that look like the old slot, except with extra
+    // content at the end of each device. This will confuse dm-verity, and
+    // ultimately we'll fail to boot. Why not make it a fatal error and have
+    // the reason be clearer? Because the indicator file still exists, and
+    // if this was FATAL, reverting to the old slot would be broken.
+    std::string old_slot;
+    auto boot_file = GetSnapshotBootIndicatorPath();
+    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
+        PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
+        return false;
+    }
+    if (device_->GetSlotSuffix() == old_slot) {
+        LOG(INFO) << "Detected slot rollback, will not mount snapshots.";
+        return false;
+    }
+
+    // If we can't read the update state, it's unlikely anything else will
+    // succeed, so this is a fatal error. We'll eventually exhaust boot
+    // attempts and revert to the old slot.
+    auto lock = LockShared();
+    if (!lock) {
+        LOG(FATAL) << "Could not read update state to determine snapshot status";
+        return false;
+    }
+    switch (ReadUpdateState(lock.get())) {
+        case UpdateState::Unverified:
+        case UpdateState::Merging:
+        case UpdateState::MergeFailed:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool SnapshotManager::CreateLogicalAndSnapshotPartitions(const std::string& super_device) {
+    LOG(INFO) << "Creating logical partitions with snapshots as needed";
+
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    std::vector<std::string> snapshot_list;
+    if (!ListSnapshots(lock.get(), &snapshot_list)) {
+        return false;
+    }
+
+    std::unordered_set<std::string> live_snapshots;
+    for (const auto& snapshot : snapshot_list) {
+        SnapshotStatus status;
+        if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {
+            return false;
+        }
+        if (status.state != SnapshotState::MergeCompleted) {
+            live_snapshots.emplace(snapshot);
+        }
+    }
+
+    const auto& opener = device_->GetPartitionOpener();
+    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
+    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);
+    if (!metadata) {
+        LOG(ERROR) << "Could not read dynamic partition metadata for device: " << super_device;
+        return false;
+    }
+
+    // Map logical partitions.
+    auto& dm = DeviceMapper::Instance();
+    for (const auto& partition : metadata->partitions) {
+        auto partition_name = GetPartitionName(partition);
+        if (!partition.num_extents) {
+            LOG(INFO) << "Skipping zero-length logical partition: " << partition_name;
+            continue;
+        }
+
+        CreateLogicalPartitionParams params = {
+                .block_device = super_device,
+                .metadata = metadata.get(),
+                .partition = &partition,
+                .partition_opener = &opener,
+        };
+
+        if (auto iter = live_snapshots.find(partition_name); iter != live_snapshots.end()) {
+            // If the device has a snapshot, it'll need to be writable, and
+            // we'll need to create the logical partition with a marked-up name
+            // (since the snapshot will use the partition name).
+            params.force_writable = true;
+            params.device_name = GetBaseDeviceName(partition_name);
+        }
+
+        std::string ignore_path;
+        if (!CreateLogicalPartition(params, &ignore_path)) {
+            LOG(ERROR) << "Could not create logical partition " << partition_name << " as device "
+                       << params.GetDeviceName();
+            return false;
+        }
+        if (!params.force_writable) {
+            // No snapshot.
+            continue;
+        }
+
+        // We don't have ueventd in first-stage init, so use device major:minor
+        // strings instead.
+        std::string base_device;
+        if (!dm.GetDeviceString(params.GetDeviceName(), &base_device)) {
+            LOG(ERROR) << "Could not determine major/minor for: " << params.GetDeviceName();
+            return false;
+        }
+        if (!MapSnapshot(lock.get(), partition_name, base_device, {}, &ignore_path)) {
+            LOG(ERROR) << "Could not map snapshot for partition: " << partition_name;
+            return false;
+        }
+    }
+    return true;
+}
+
+auto SnapshotManager::OpenFile(const std::string& file, int open_flags, int lock_flags)
+        -> std::unique_ptr<LockedFile> {
+    unique_fd fd(open(file.c_str(), open_flags | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0660));
+    if (fd < 0) {
+        PLOG(ERROR) << "Open failed: " << file;
+        return nullptr;
+    }
+    if (flock(fd, lock_flags) < 0) {
+        PLOG(ERROR) << "Acquire flock failed: " << file;
+        return nullptr;
+    }
+    // For simplicity, we want to CHECK that lock_mode == LOCK_EX, in some
+    // calls, so strip extra flags.
+    int lock_mode = lock_flags & (LOCK_EX | LOCK_SH);
+    return std::make_unique<LockedFile>(file, std::move(fd), lock_mode);
+}
+
+SnapshotManager::LockedFile::~LockedFile() {
+    if (flock(fd_, LOCK_UN) < 0) {
+        PLOG(ERROR) << "Failed to unlock file: " << path_;
+    }
+}
+
+std::string SnapshotManager::GetStateFilePath() const {
+    return metadata_dir_ + "/state"s;
+}
+
+std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::OpenStateFile(int open_flags,
+                                                                            int lock_flags) {
+    auto state_file = GetStateFilePath();
+    return OpenFile(state_file, open_flags, lock_flags);
+}
+
+std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::LockShared() {
+    return OpenStateFile(O_RDONLY, LOCK_SH);
+}
+
+std::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::LockExclusive() {
+    return OpenStateFile(O_RDWR | O_CREAT, LOCK_EX);
+}
+
+UpdateState SnapshotManager::ReadUpdateState(LockedFile* file) {
+    // Reset position since some calls read+write.
+    if (lseek(file->fd(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek state file failed";
+        return UpdateState::None;
+    }
+
+    std::string contents;
+    if (!android::base::ReadFdToString(file->fd(), &contents)) {
+        PLOG(ERROR) << "Read state file failed";
+        return UpdateState::None;
+    }
+
+    if (contents.empty() || contents == "none") {
+        return UpdateState::None;
+    } else if (contents == "initiated") {
+        return UpdateState::Initiated;
+    } else if (contents == "unverified") {
+        return UpdateState::Unverified;
+    } else if (contents == "merging") {
+        return UpdateState::Merging;
+    } else if (contents == "merge-completed") {
+        return UpdateState::MergeCompleted;
+    } else if (contents == "merge-needs-reboot") {
+        return UpdateState::MergeNeedsReboot;
+    } else if (contents == "merge-failed") {
+        return UpdateState::MergeFailed;
+    } else {
+        LOG(ERROR) << "Unknown merge state in update state file";
+        return UpdateState::None;
+    }
+}
+
+bool SnapshotManager::WriteUpdateState(LockedFile* file, UpdateState state) {
+    std::string contents;
+    switch (state) {
+        case UpdateState::None:
+            contents = "none";
+            break;
+        case UpdateState::Initiated:
+            contents = "initiated";
+            break;
+        case UpdateState::Unverified:
+            contents = "unverified";
+            break;
+        case UpdateState::Merging:
+            contents = "merging";
+            break;
+        case UpdateState::MergeCompleted:
+            contents = "merge-completed";
+            break;
+        case UpdateState::MergeNeedsReboot:
+            contents = "merge-needs-reboot";
+            break;
+        case UpdateState::MergeFailed:
+            contents = "merge-failed";
+            break;
+        default:
+            LOG(ERROR) << "Unknown update state";
+            return false;
+    }
+
+    if (!Truncate(file)) return false;
+    if (!android::base::WriteStringToFd(contents, file->fd())) {
+        PLOG(ERROR) << "Could not write to state file";
+        return false;
+    }
+    return true;
+}
+
+std::string SnapshotManager::GetSnapshotStatusFilePath(const std::string& name) {
+    auto file = metadata_dir_ + "/snapshots/"s + name;
+    return file;
+}
+
+bool SnapshotManager::ReadSnapshotStatus(LockedFile* lock, const std::string& name,
+                                         SnapshotStatus* status) {
+    CHECK(lock);
+    auto path = GetSnapshotStatusFilePath(name);
+
+    unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+    if (fd < 0) {
+        PLOG(ERROR) << "Open failed: " << path;
+        return false;
+    }
+
+    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() != 5) {
+        LOG(ERROR) << "Invalid status line for snapshot: " << path;
+        return false;
+    }
+
+    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;
+    }
+
+    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->sectors_allocated)) {
+        LOG(ERROR) << "Invalid snapshot size in status line for: " << path;
+        return false;
+    }
+    if (!android::base::ParseUint(pieces[4], &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::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) {
+    // The caller must take an exclusive lock to modify snapshots.
+    CHECK(lock);
+    CHECK(lock->lock_mode() == LOCK_EX);
+
+    auto path = GetSnapshotStatusFilePath(name);
+    unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW | O_CREAT | O_SYNC, 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.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;
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::Truncate(LockedFile* file) {
+    if (lseek(file->fd(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek file failed: " << file->path();
+        return false;
+    }
+    if (ftruncate(file->fd(), 0) < 0) {
+        PLOG(ERROR) << "truncate failed: " << file->path();
+        return false;
+    }
+    return true;
+}
+
+std::string SnapshotManager::GetSnapshotDeviceName(const std::string& snapshot_name,
+                                                   const SnapshotStatus& status) {
+    if (status.device_size != status.snapshot_size) {
+        return snapshot_name + "-inner";
+    }
+    return snapshot_name;
+}
+
+bool SnapshotManager::EnsureImageManager() {
+    if (images_) return true;
+
+    // For now, use a preset timeout.
+    images_ = android::fiemap::IImageManager::Open(gsid_dir_, 15000ms);
+    if (!images_) {
+        LOG(ERROR) << "Could not open ImageManager";
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::ForceLocalImageManager() {
+    images_ = android::fiemap::ImageManager::Open(gsid_dir_);
+    if (!images_) {
+        LOG(ERROR) << "Could not open ImageManager";
+        return false;
+    }
+    has_local_image_manager_ = true;
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
new file mode 100644
index 0000000..f4eb1ac
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -0,0 +1,477 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <libsnapshot/snapshot.h>
+
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <chrono>
+#include <iostream>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <fs_mgr_dm_linear.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+#include <liblp/builder.h>
+
+#include "test_helpers.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::fiemap::IImageManager;
+using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::MetadataBuilder;
+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).
+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()) {}
+
+    // This is exposed for main.
+    void Cleanup() {
+        InitializeState();
+        CleanupTestArtifacts();
+    }
+
+  protected:
+    void SetUp() override {
+        InitializeState();
+        CleanupTestArtifacts();
+        FormatFakeSuper();
+
+        ASSERT_TRUE(sm->BeginUpdate());
+    }
+
+    void TearDown() override {
+        lock_ = nullptr;
+
+        CleanupTestArtifacts();
+    }
+
+    void InitializeState() {
+        ASSERT_TRUE(sm->EnsureImageManager());
+        image_manager_ = sm->image_manager();
+
+        test_device->set_slot_suffix("_a");
+    }
+
+    void CleanupTestArtifacts() {
+        // Normally cancelling inside a merge is not allowed. Since these
+        // are tests, we don't care, destroy everything that might exist.
+        // Note we hardcode this list because of an annoying quirk: when
+        // completing a merge, the snapshot stops existing, so we can't
+        // get an accurate list to remove.
+        lock_ = nullptr;
+
+        std::vector<std::string> snapshots = {"test-snapshot", "test_partition_b"};
+        for (const auto& snapshot : snapshots) {
+            DeleteSnapshotDevice(snapshot);
+            DeleteBackingImage(image_manager_, snapshot + "-cow");
+
+            auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
+            android::base::RemoveFileIfExists(status_file);
+        }
+
+        // Remove stale partitions in fake super.
+        std::vector<std::string> partitions = {
+                "base-device",
+                "test_partition_b",
+                "test_partition_b-base",
+        };
+        for (const auto& partition : partitions) {
+            DeleteDevice(partition);
+        }
+
+        if (sm->GetUpdateState() != UpdateState::None) {
+            auto state_file = sm->GetStateFilePath();
+            unlink(state_file.c_str());
+        }
+    }
+
+    bool AcquireLock() {
+        lock_ = sm->OpenStateFile(O_RDWR, LOCK_EX);
+        return !!lock_;
+    }
+
+    // This is so main() can instantiate this to invoke Cleanup.
+    virtual void TestBody() override {}
+
+    void FormatFakeSuper() {
+        BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4096);
+        std::vector<BlockDeviceInfo> devices = {super_device};
+
+        auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
+        ASSERT_NE(builder, nullptr);
+
+        auto metadata = builder->Export();
+        ASSERT_NE(metadata, nullptr);
+
+        TestPartitionOpener opener(fake_super);
+        ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *metadata.get()));
+    }
+
+    // If |path| is non-null, the partition will be mapped after creation.
+    bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) {
+        TestPartitionOpener opener(fake_super);
+        auto builder = MetadataBuilder::New(opener, "super", 0);
+        if (!builder) return false;
+
+        auto partition = builder->AddPartition(name, 0);
+        if (!partition) return false;
+        if (!builder->ResizePartition(partition, size)) {
+            return false;
+        }
+
+        // Update both slots for convenience.
+        auto metadata = builder->Export();
+        if (!metadata) return false;
+        if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0) ||
+            !UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
+            return false;
+        }
+
+        if (!path) return true;
+
+        CreateLogicalPartitionParams params = {
+                .block_device = fake_super,
+                .metadata = metadata.get(),
+                .partition_name = name,
+                .force_writable = true,
+                .timeout_ms = 10s,
+        };
+        return CreateLogicalPartition(params, path);
+    }
+
+    void DeleteSnapshotDevice(const std::string& snapshot) {
+        DeleteDevice(snapshot);
+        DeleteDevice(snapshot + "-inner");
+    }
+    void DeleteDevice(const std::string& device) {
+        if (dm_.GetState(device) != DmDeviceState::INVALID) {
+            ASSERT_TRUE(dm_.DeleteDevice(device));
+        }
+    }
+
+    DeviceMapper& dm_;
+    std::unique_ptr<SnapshotManager::LockedFile> lock_;
+    android::fiemap::IImageManager* image_manager_ = nullptr;
+    std::string fake_super_;
+};
+
+TEST_F(SnapshotTest, CreateSnapshot) {
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+
+    std::vector<std::string> snapshots;
+    ASSERT_TRUE(sm->ListSnapshots(lock_.get(), &snapshots));
+    ASSERT_EQ(snapshots.size(), 1);
+    ASSERT_EQ(snapshots[0], "test-snapshot");
+
+    // Scope so delete can re-acquire the snapshot file lock.
+    {
+        SnapshotManager::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_TRUE(sm->UnmapSnapshot(lock_.get(), "test-snapshot"));
+    ASSERT_TRUE(sm->DeleteSnapshot(lock_.get(), "test-snapshot"));
+}
+
+TEST_F(SnapshotTest, MapSnapshot) {
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+
+    std::string base_device;
+    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+
+    std::string snap_device;
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+    ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
+}
+
+TEST_F(SnapshotTest, MapPartialSnapshot) {
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kSnapshotSize = 1024 * 1024;
+    static const uint64_t kDeviceSize = 1024 * 1024 * 2;
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kSnapshotSize,
+                                   kSnapshotSize));
+
+    std::string base_device;
+    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+
+    std::string snap_device;
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+    ASSERT_TRUE(android::base::StartsWith(snap_device, "/dev/block/dm-"));
+}
+
+TEST_F(SnapshotTest, NoMergeBeforeReboot) {
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Merge should fail, since the slot hasn't changed.
+    ASSERT_FALSE(sm->InitiateMerge());
+}
+
+TEST_F(SnapshotTest, CleanFirstStageMount) {
+    // If there's no update in progress, there should be no first-stage mount
+    // needed.
+    TestDeviceInfo* info = new TestDeviceInfo(fake_super);
+    auto sm = SnapshotManager::NewForFirstStageMount(info);
+    ASSERT_NE(sm, nullptr);
+    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
+}
+
+TEST_F(SnapshotTest, FirstStageMountAfterRollback) {
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // We didn't change the slot, so we shouldn't need snapshots.
+    TestDeviceInfo* info = new TestDeviceInfo(fake_super);
+    auto sm = SnapshotManager::NewForFirstStageMount(info);
+    ASSERT_NE(sm, nullptr);
+    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
+}
+
+TEST_F(SnapshotTest, Merge) {
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+
+    std::string base_device, snap_device;
+    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+
+    std::string test_string = "This is a test string.";
+    {
+        unique_fd fd(open(snap_device.c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
+        ASSERT_GE(fd, 0);
+        ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
+    }
+
+    // Note: we know the name of the device is test-snapshot because we didn't
+    // request a linear segment.
+    DeviceMapper::TargetInfo target;
+    ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+
+    // Release the lock.
+    lock_ = nullptr;
+
+    // Done updating.
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    test_device->set_slot_suffix("_b");
+    ASSERT_TRUE(sm->InitiateMerge());
+
+    // The device should have been switched to a snapshot-merge target.
+    ASSERT_TRUE(sm->IsSnapshotDevice("test-snapshot", &target));
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
+
+    // We should not be able to cancel an update now.
+    ASSERT_FALSE(sm->CancelUpdate());
+
+    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
+    ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
+
+    // The device should no longer be a snapshot or snapshot-merge.
+    ASSERT_FALSE(sm->IsSnapshotDevice("test-snapshot"));
+
+    // Test that we can read back the string we wrote to the snapshot.
+    unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(fd, 0);
+
+    std::string buffer(test_string.size(), '\0');
+    ASSERT_TRUE(android::base::ReadFully(fd, buffer.data(), buffer.size()));
+    ASSERT_EQ(test_string, buffer);
+}
+
+TEST_F(SnapshotTest, MergeCannotRemoveCow) {
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test-snapshot", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+
+    std::string base_device, snap_device;
+    ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
+    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+
+    // Keep an open handle to the cow device. This should cause the merge to
+    // be incomplete.
+    auto cow_path = android::base::GetProperty("gsid.mapped_image.test-snapshot-cow", "");
+    unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(fd, 0);
+
+    // Release the lock.
+    lock_ = nullptr;
+
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    test_device->set_slot_suffix("_b");
+    ASSERT_TRUE(sm->InitiateMerge());
+
+    // COW cannot be removed due to open fd, so expect a soft failure.
+    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeNeedsReboot);
+
+    // Forcefully delete the snapshot device, so it looks like we just rebooted.
+    DeleteSnapshotDevice("test-snapshot");
+
+    // Map snapshot should fail now, because we're in a merge-complete state.
+    ASSERT_TRUE(AcquireLock());
+    ASSERT_FALSE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
+
+    // Release everything and now the merge should complete.
+    fd = {};
+    lock_ = nullptr;
+
+    ASSERT_EQ(sm->WaitForMerge(), UpdateState::MergeCompleted);
+}
+
+TEST_F(SnapshotTest, FirstStageMountAndMerge) {
+    ASSERT_TRUE(AcquireLock());
+
+    static const uint64_t kDeviceSize = 1024 * 1024;
+
+    ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
+    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize,
+                                   kDeviceSize));
+
+    // Simulate a reboot into the new slot.
+    lock_ = nullptr;
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    auto rebooted = new TestDeviceInfo(fake_super);
+    rebooted->set_slot_suffix("_b");
+
+    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+
+    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);
+
+    DeviceMapper::TargetInfo target;
+    auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status);
+    ASSERT_TRUE(init->IsSnapshotDevice(dm_name, &target));
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+using namespace android::snapshot;
+
+bool Mkdir(const std::string& path) {
+    if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
+        std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
+        return false;
+    }
+    return true;
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    std::vector<std::string> paths = {
+            // clang-format off
+            "/data/gsi/ota/test",
+            "/data/gsi/ota/test/super",
+            "/metadata/gsi/ota/test",
+            "/metadata/gsi/ota/test/super",
+            "/metadata/ota/test",
+            "/metadata/ota/test/snapshots",
+            // clang-format on
+    };
+    for (const auto& path : paths) {
+        if (!Mkdir(path)) {
+            return 1;
+        }
+    }
+
+    // Create this once, otherwise, gsid will start/stop between each test.
+    test_device = new TestDeviceInfo();
+    sm = SnapshotManager::New(test_device);
+    if (!sm) {
+        std::cerr << "Could not create snapshot manager\n";
+        return 1;
+    }
+
+    // Clean up previous run.
+    SnapshotTest().Cleanup();
+
+    // Use a separate image manager for our fake super partition.
+    auto super_images = IImageManager::Open("ota/test/super", 10s);
+    if (!super_images) {
+        std::cerr << "Could not create image manager\n";
+        return 1;
+    }
+
+    // Clean up any old copy.
+    DeleteBackingImage(super_images.get(), "fake-super");
+
+    // Create and map the fake super partition.
+    static constexpr int kImageFlags =
+            IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
+    if (!super_images->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
+        std::cerr << "Could not create fake super partition\n";
+        return 1;
+    }
+    if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) {
+        std::cerr << "Could not map fake super partition\n";
+        return 1;
+    }
+    test_device->set_fake_super(fake_super);
+
+    auto result = RUN_ALL_TESTS();
+
+    DeleteBackingImage(super_images.get(), "fake-super");
+
+    return result;
+}
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
new file mode 100644
index 0000000..f67dd21
--- /dev/null
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test_helpers.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace snapshot {
+
+using android::fiemap::IImageManager;
+
+void DeleteBackingImage(IImageManager* manager, const std::string& name) {
+    if (manager->IsImageMapped(name)) {
+        ASSERT_TRUE(manager->UnmapImageDevice(name));
+    }
+    if (manager->BackingImageExists(name)) {
+        ASSERT_TRUE(manager->DeleteBackingImage(name));
+    }
+}
+
+android::base::unique_fd TestPartitionOpener::Open(const std::string& partition_name,
+                                                   int flags) const {
+    if (partition_name == "super") {
+        return PartitionOpener::Open(fake_super_path_, flags);
+    }
+    return PartitionOpener::Open(partition_name, flags);
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name,
+                                  android::fs_mgr::BlockDeviceInfo* info) const {
+    if (partition_name == "super") {
+        return PartitionOpener::GetInfo(fake_super_path_, info);
+    }
+    return PartitionOpener::GetInfo(partition_name, info);
+}
+
+std::string TestPartitionOpener::GetDeviceString(const std::string& partition_name) const {
+    if (partition_name == "super") {
+        return fake_super_path_;
+    }
+    return PartitionOpener::GetDeviceString(partition_name);
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
new file mode 100644
index 0000000..c87f118
--- /dev/null
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <libfiemap/image_manager.h>
+#include <liblp/partition_opener.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace std::string_literals;
+
+// Redirect requests for "super" to our fake super partition.
+class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
+  public:
+    explicit TestPartitionOpener(const std::string& fake_super_path)
+        : fake_super_path_(fake_super_path) {}
+
+    android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+    bool GetInfo(const std::string& partition_name,
+                 android::fs_mgr::BlockDeviceInfo* info) const override;
+    std::string GetDeviceString(const std::string& partition_name) const override;
+
+  private:
+    std::string fake_super_path_;
+};
+
+class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
+  public:
+    TestDeviceInfo() {}
+    explicit TestDeviceInfo(const std::string& fake_super) { set_fake_super(fake_super); }
+    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_; }
+    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
+        return *opener_.get();
+    }
+
+    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);
+    }
+
+  private:
+    std::string slot_suffix_ = "_a";
+    std::unique_ptr<TestPartitionOpener> opener_;
+};
+
+// Helper for error-spam-free cleanup.
+void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index c2a0f33..ed6d0e3 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -15,10 +15,13 @@
 
 adb remount tests
 
---help        This help
---serial      Specify device (must if multiple are present)
---color       Dress output with highlighting colors
---print-time  Report the test duration
+--color                     Dress output with highlighting colors
+--help                      This help
+--no-wait-screen            Do not wait for display screen to settle
+--print-time                Report the test duration
+--serial                    Specify device (must if multiple are present)
+--wait-adb <duration>       adb wait timeout
+--wait-fastboot <duration>  fastboot wait timeout
 
 Conditions:
  - Must be a userdebug build.
@@ -53,6 +56,7 @@
 
 ADB_WAIT=4m
 FASTBOOT_WAIT=2m
+screen_wait=true
 
 ##
 ##  Helper Functions
@@ -185,7 +189,7 @@
 [ "USAGE: adb_cat <file> >stdout
 
 Returns: content of file to stdout with carriage returns skipped,
-         true of the file exists" ]
+         true if the file exists" ]
 adb_cat() {
     local OUTPUT="`adb_sh cat ${1} </dev/null 2>&1`"
     local ret=${?}
@@ -193,6 +197,17 @@
     return ${ret}
 }
 
+[ "USAGE: adb_ls <dirfile> >stdout
+
+Returns: filename or directoru content to stdout with carriage returns skipped,
+         true if the ls had no errors" ]
+adb_ls() {
+    local OUTPUT="`adb_sh ls ${1} </dev/null 2>/dev/null`"
+    local ret=${?}
+    echo "${OUTPUT}" | tr -d '\r'
+    return ${ret}
+}
+
 [ "USAGE: adb_reboot
 
 Returns: true if the reboot command succeeded" ]
@@ -436,6 +451,10 @@
 -n - echo newline at exit
 TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
 wait_for_screen() {
+  if ! ${screen_wait}; then
+    adb_wait
+    return
+  fi
   exit_function=true
   if [ X"-n" = X"${1}" ]; then
     exit_function=echo
@@ -690,8 +709,9 @@
       EXPECT_EQ "${lval}" "${rval}" ${*}
       return
   fi
-  EXPECT_EQ "${lval}" "${rval}" ||
+  if ! EXPECT_EQ "${lval}" "${rval}"; then
     die "${@}"
+  fi
 }
 
 [ "USAGE: check_ne <lval> <rval> [--warning [message]]
@@ -705,8 +725,9 @@
       EXPECT_NE "${lval}" "${rval}" ${*}
       return
   fi
-  EXPECT_NE "${lval}" "${rval}" ||
+  if ! EXPECT_NE "${lval}" "${rval}"; then
     die "${@}"
+  fi
 }
 
 [ "USAGE: skip_administrative_mounts [data] < /proc/mounts
@@ -743,6 +764,9 @@
 
 OPTIONS=`getopt --alternative --unquoted \
                 --longoptions help,serial:,colour,color,no-colour,no-color \
+                --longoptions wait-adb:,wait-fastboot: \
+                --longoptions wait-screen,wait-display \
+                --longoptions no-wait-screen,no-wait-display \
                 --longoptions gtest_print_time,print-time \
                 -- "?hs:" ${*}` ||
   ( echo "${USAGE}" >&2 ; false ) ||
@@ -766,9 +790,23 @@
     --no-color | --no-colour)
       color=false
       ;;
+    --no-wait-display | --no-wait-screen)
+      screen_wait=false
+      ;;
+    --wait-display | --wait-screen)
+      screen_wait=true
+      ;;
     --print-time | --gtest_print_time)
       print_time=true
       ;;
+    --wait-adb)
+      ADB_WAIT=${2}
+      shift
+      ;;
+    --wait-fastboot)
+      FASTBOOT_WAIT=${2}
+      shift
+      ;;
     --)
       shift
       break
@@ -811,6 +849,8 @@
 
 # Do something.
 
+# Collect characteristics of the device and report.
+
 D=`get_property ro.serialno`
 [ -n "${D}" ] || D=`get_property ro.boot.serialno`
 [ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
@@ -833,35 +873,78 @@
 [ -z "${ACTIVE_SLOT}" ] ||
   echo "${BLUE}[     INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
 
+# Acquire list of system partitions
+
+PARTITIONS=`adb_su cat /vendor/etc/fstab* |
+              skip_administrative_mounts |
+              sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
+              sort -u |
+              tr '\n' ' '`
+PARTITIONS="${PARTITIONS:-system vendor}"
+# KISS (we do not support sub-mounts for system partitions currently)
+MOUNTS="`for i in ${PARTITIONS}; do
+           echo /${i}
+         done |
+         tr '\n' ' '`"
+echo "${BLUE}[     INFO ]${NORMAL} System Partitions list: ${PARTITIONS}" >&2
+
 # Report existing partition sizes
-adb_sh ls -l /dev/block/by-name/ </dev/null 2>/dev/null |
+adb_sh ls -l /dev/block/by-name/ /dev/block/mapper/ </dev/null 2>/dev/null |
   sed -n 's@.* \([^ ]*\) -> /dev/block/\([^ ]*\)$@\1 \2@p' |
   while read name device; do
-    case ${name} in
-      system_[ab] | system | vendor_[ab] | vendor | super | cache)
-        case ${device} in
-          sd*)
-            device=${device%%[0-9]*}/${device}
-            ;;
-        esac
-        size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
-          size=`expr ${size} / 2` &&
-          echo "${BLUE}[     INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+    [ super = ${name} -o cache = ${name} ] ||
+      (
+        for i in ${PARTITIONS}; do
+          [ ${i} = ${name} -o ${i} = ${name%_[ab]} ] && exit
+        done
+        exit 1
+      ) ||
+      continue
+
+    case ${device} in
+      sd*)
+        device=${device%%[0-9]*}/${device}
         ;;
     esac
+    size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
+      size=`expr ${size} / 2` &&
+      echo "${BLUE}[     INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
   done
 
 # If reboot too soon after fresh flash, could trip device update failure logic
 wait_for_screen
 # Can we test remount -R command?
+OVERLAYFS_BACKING="cache mnt/scratch"
 overlayfs_supported=true
-if [ "orange" = "`get_property ro.boot.verifiedbootstate`" -a \
-     "2" = "`get_property partition.system.verified`" ]; then
+if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
+     "2" != "`get_property partition.system.verified`" ]; then
   restore() {
     ${overlayfs_supported} || return 0
     inFastboot &&
       fastboot reboot &&
-      adb_wait ${ADB_WAIT}
+      adb_wait ${ADB_WAIT} ||
+      true
+    if inAdb; then
+      reboot=false
+      for d in ${OVERLAYFS_BACKING}; do
+        if adb_su ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
+          adb_su rm -rf /${d}/overlay </dev/null
+          reboot=true
+        fi
+      done
+      if ${reboot}; then
+        adb_reboot &&
+        adb_wait ${ADB_WAIT}
+      fi
+    fi
+  }
+else
+  restore() {
+    ${overlayfs_supported} || return 0
+    inFastboot &&
+      fastboot reboot &&
+      adb_wait ${ADB_WAIT} ||
+      true
     inAdb &&
       adb_root &&
       adb enable-verity >/dev/null 2>/dev/null &&
@@ -920,7 +1003,6 @@
 # So lets do our best to surgically wipe the overlayfs state without
 # having to go through enable-verity transition.
 reboot=false
-OVERLAYFS_BACKING="cache mnt/scratch"
 for d in ${OVERLAYFS_BACKING}; do
   if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>/dev/null; then
     echo "${ORANGE}[  WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
@@ -1049,7 +1131,7 @@
 
 # Feed log with selinux denials as baseline before overlays
 adb_unroot
-adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
 adb_root
 
 D=`adb remount 2>&1`
@@ -1141,17 +1223,19 @@
 
 # Check something.
 
-echo "${GREEN}[ RUN      ]${NORMAL} push content to /system and /vendor" >&2
+echo "${GREEN}[ RUN      ]${NORMAL} push content to ${MOUNTS}" >&2
 
 A="Hello World! $(date)"
-echo "${A}" | adb_sh cat - ">/system/hello"
-echo "${A}" | adb_sh cat - ">/vendor/hello"
-B="`adb_cat /system/hello`" ||
-  die "sytem hello"
-check_eq "${A}" "${B}" /system before reboot
-B="`adb_cat /vendor/hello`" ||
-  die "vendor hello"
-check_eq "${A}" "${B}" /vendor before reboot
+for i in ${MOUNTS}; do
+  echo "${A}" | adb_sh cat - ">${i}/hello"
+  B="`adb_cat ${i}/hello`" ||
+    die "${i#/} hello"
+  check_eq "${A}" "${B}" ${i} before reboot
+done
+echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
+B="`adb_cat /system/priv-app/hello`" ||
+  die "system priv-app hello"
+check_eq "${A}" "${B}" /system/priv-app before reboot
 SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
 VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
 SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
@@ -1226,17 +1310,23 @@
   echo "${GREEN}[       OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
   # Feed unprivileged log with selinux denials as a result of overlays
   wait_for_screen
-  adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+  adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
 fi
-B="`adb_cat /system/hello`"
-check_eq "${A}" "${B}" /system after reboot
-echo "${GREEN}[       OK ]${NORMAL} /system content remains after reboot" >&2
+# If overlayfs has a nested security problem, this will fail.
+B="`adb_ls /system/`" ||
+  die "adb ls /system"
+[ X"${B}" != X"${B#*priv-app}" ] ||
+  die "adb ls /system/priv-app"
+B="`adb_cat /system/priv-app/hello`"
+check_eq "${A}" "${B}" /system/priv-app after reboot
 # Only root can read vendor if sepolicy permissions are as expected.
 adb_root ||
   die "adb root"
-B="`adb_cat /vendor/hello`"
-check_eq "${A}" "${B}" vendor after reboot
-echo "${GREEN}[       OK ]${NORMAL} /vendor content remains after reboot" >&2
+for i in ${MOUNTS}; do
+  B="`adb_cat ${i}/hello`"
+  check_eq "${A}" "${B}" ${i#/} after reboot
+  echo "${GREEN}[       OK ]${NORMAL} ${i} content remains after reboot" >&2
+done
 
 check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
 check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
@@ -1247,7 +1337,7 @@
 check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" devt for su after reboot
 
 # Feed log with selinux denials as a result of overlays
-adb_sh find /system /vendor </dev/null >/dev/null 2>/dev/null
+adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null
 
 # Check if the updated libc.so is persistent after reboot.
 adb_root &&
@@ -1351,6 +1441,12 @@
   fi
   B="`adb_cat /system/hello`"
   check_eq "${A}" "${B}" system after flash vendor
+  B="`adb_ls /system/`" ||
+    die "adb ls /system"
+  [ X"${B}" != X"${B#*priv-app}" ] ||
+    die "adb ls /system/priv-app"
+  B="`adb_cat /system/priv-app/hello`"
+  check_eq "${A}" "${B}" system/priv-app after flash vendor
   adb_root ||
     die "adb root"
   B="`adb_cat /vendor/hello`"
@@ -1392,15 +1488,20 @@
 echo "${H}"
 [ ${err} = 0 ] &&
   ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
-  adb_sh rm /system/hello </dev/null ||
+  adb_sh rm /system/hello /system/priv-app/hello </dev/null ||
   ( [ -n "${L}" ] && echo "${L}" && false ) ||
   die -t ${T} "cleanup hello"
 B="`adb_cat /system/hello`"
 check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
+B="`adb_cat /system/priv-app/hello`"
+check_eq "cat: /system/priv-app/hello: No such file or directory" "${B}" after rm
 B="`adb_cat /vendor/hello`"
 check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
+for i in ${MOUNTS}; do
+  adb_sh rm ${i}/hello </dev/null 2>/dev/null || true
+done
 
-if [ -n "${scratch_partition}" ]; then
+if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
 
   echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
 
@@ -1413,7 +1514,7 @@
   }
   dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
     fastboot_wait ${FASTBOOT_WAIT} ||
-    die "reboot into fastboot `usb_status`"
+    die "reboot into fastboot to flash scratch `usb_status`"
   fastboot flash --force ${scratch_partition} ${img}
   err=${?}
   cleanup
@@ -1542,7 +1643,9 @@
     adb_wait ${ADB_WAIT} ||
     die "adb remount -R"
   if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
-       "2" = "`get_property partition.system.verified`" ]; then
+       "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"
   fi
 
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 7e6ad5b..2738457 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -49,7 +49,10 @@
     std::cerr << "  delete <dm-name>" << std::endl;
     std::cerr << "  list <devices | targets> [-v]" << std::endl;
     std::cerr << "  getpath <dm-name>" << std::endl;
+    std::cerr << "  info <dm-name>" << std::endl;
     std::cerr << "  status <dm-name>" << std::endl;
+    std::cerr << "  resume <dm-name>" << std::endl;
+    std::cerr << "  suspend <dm-name>" << std::endl;
     std::cerr << "  table <dm-name>" << std::endl;
     std::cerr << "  help" << std::endl;
     std::cerr << std::endl;
@@ -194,19 +197,12 @@
     char** argv_;
 };
 
-static int DmCreateCmdHandler(int argc, char** argv) {
-    if (argc < 1) {
-        std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
-        return -EINVAL;
-    }
-    std::string name = argv[0];
-
+static bool parse_table_args(DmTable* table, int argc, char** argv) {
     // Parse extended options first.
-    DmTable table;
     int arg_index = 1;
     while (arg_index < argc && argv[arg_index][0] == '-') {
         if (strcmp(argv[arg_index], "-ro") == 0) {
-            table.set_readonly(true);
+            table->set_readonly(true);
             arg_index++;
         } else {
             std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
@@ -218,15 +214,30 @@
     TargetParser parser(argc - arg_index, argv + arg_index);
     while (parser.More()) {
         std::unique_ptr<DmTarget> target = parser.Next();
-        if (!target || !table.AddTarget(std::move(target))) {
+        if (!target || !table->AddTarget(std::move(target))) {
             return -EINVAL;
         }
     }
 
-    if (table.num_targets() == 0) {
+    if (table->num_targets() == 0) {
         std::cerr << "Must define at least one target." << std::endl;
         return -EINVAL;
     }
+    return 0;
+}
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+        return -EINVAL;
+    }
+    std::string name = argv[0];
+
+    DmTable table;
+    int ret = parse_table_args(&table, argc, argv);
+    if (ret) {
+        return ret;
+    }
 
     DeviceMapper& dm = DeviceMapper::Instance();
     if (!dm.CreateDevice(name, table)) {
@@ -252,6 +263,27 @@
     return 0;
 }
 
+static int DmReplaceCmdHandler(int argc, char** argv) {
+    if (argc < 1) {
+        std::cerr << "Usage: dmctl replace <dm-name> <targets...>" << std::endl;
+        return -EINVAL;
+    }
+    std::string name = argv[0];
+
+    DmTable table;
+    int ret = parse_table_args(&table, argc, argv);
+    if (ret) {
+        return ret;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.LoadTableAndActivate(name, table)) {
+        std::cerr << "Failed to replace device-mapper table to: " << name << std::endl;
+        return -EIO;
+    }
+    return 0;
+}
+
 static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
                          [[maybe_unused]] char** argv) {
     std::vector<DmTargetTypeInfo> targets;
@@ -357,6 +389,41 @@
     return 0;
 }
 
+static int InfoCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    auto info = dm.GetDetailedInfo(argv[0]);
+    if (!info) {
+        std::cerr << "Invalid device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+
+    constexpr int spacing = 14;
+    std::cout << std::left << std::setw(spacing) << "device"
+              << ": " << argv[0] << std::endl;
+    std::cout << std::left << std::setw(spacing) << "active"
+              << ": " << std::boolalpha << !info->IsSuspended() << std::endl;
+    std::cout << std::left << std::setw(spacing) << "access"
+              << ": ";
+    if (info->IsReadOnly()) {
+        std::cout << "ro ";
+    } else {
+        std::cout << "rw ";
+    }
+    std::cout << std::endl;
+    std::cout << std::left << std::setw(spacing) << "activeTable"
+              << ": " << std::boolalpha << info->IsActiveTablePresent() << std::endl;
+    std::cout << std::left << std::setw(spacing) << "inactiveTable"
+              << ": " << std::boolalpha << info->IsInactiveTablePresent() << std::endl;
+    std::cout << std::left << std::setw(spacing) << "bufferFull"
+              << ": " << std::boolalpha << info->IsBufferFull() << std::endl;
+    return 0;
+}
+
 static int DumpTable(const std::string& mode, int argc, char** argv) {
     if (argc != 1) {
         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
@@ -399,15 +466,47 @@
     return DumpTable("status", argc, argv);
 }
 
+static int ResumeCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.ChangeState(argv[0], DmDeviceState::ACTIVE)) {
+        std::cerr << "Could not resume device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int SuspendCmdHandler(int argc, char** argv) {
+    if (argc != 1) {
+        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+        return -EINVAL;
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    if (!dm.ChangeState(argv[0], DmDeviceState::SUSPENDED)) {
+        std::cerr << "Could not suspend device \"" << argv[0] << "\"." << std::endl;
+        return -EINVAL;
+    }
+    return 0;
+}
+
 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
         // clang-format off
         {"create", DmCreateCmdHandler},
         {"delete", DmDeleteCmdHandler},
+        {"replace", DmReplaceCmdHandler},
         {"list", DmListCmdHandler},
         {"help", HelpCmdHandler},
         {"getpath", GetPathCmdHandler},
+        {"info", InfoCmdHandler},
         {"table", TableCmdHandler},
         {"status", StatusCmdHandler},
+        {"resume", ResumeCmdHandler},
+        {"suspend", SuspendCmdHandler},
         // clang-format on
 };
 
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 2b7db79..778e08c 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -23,8 +23,6 @@
         "-Wunused",
     ],
     srcs: [
-        "SoftGateKeeperDevice.cpp",
-        "IGateKeeperService.cpp",
         "gatekeeperd.cpp",
     ],
 
@@ -43,9 +41,44 @@
         "libhidltransport",
         "libhwbinder",
         "android.hardware.gatekeeper@1.0",
+        "libgatekeeper_aidl",
     ],
 
     static_libs: ["libscrypt_static"],
     include_dirs: ["external/scrypt/lib/crypto"],
     init_rc: ["gatekeeperd.rc"],
 }
+
+filegroup {
+    name: "gatekeeper_aidl",
+    srcs: [
+        "binder/android/service/gatekeeper/IGateKeeperService.aidl",
+    ],
+    path: "binder",
+}
+
+cc_library_shared {
+    name: "libgatekeeper_aidl",
+    srcs: [
+        ":gatekeeper_aidl",
+        "GateKeeperResponse.cpp",
+    ],
+    aidl: {
+        export_aidl_headers: true,
+        include_dirs: [
+            "system/core/gatekeeperd/binder",
+            "frameworks/base/core/java/",
+        ],
+    },
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    export_shared_lib_headers: [
+        "libbinder",
+    ],
+}
diff --git a/gatekeeperd/GateKeeperResponse.cpp b/gatekeeperd/GateKeeperResponse.cpp
new file mode 100644
index 0000000..ca0c98f
--- /dev/null
+++ b/gatekeeperd/GateKeeperResponse.cpp
@@ -0,0 +1,83 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "gatekeeperd"
+
+#include <gatekeeper/GateKeeperResponse.h>
+
+#include <binder/Parcel.h>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace service {
+namespace gatekeeper {
+
+status_t GateKeeperResponse::readFromParcel(const Parcel* in) {
+    if (in == nullptr) {
+        LOG(ERROR) << "readFromParcel got null in parameter";
+        return BAD_VALUE;
+    }
+    timeout_ = 0;
+    should_reenroll_ = false;
+    payload_ = {};
+    response_code_ = ResponseCode(in->readInt32());
+    if (response_code_ == ResponseCode::OK) {
+        should_reenroll_ = in->readInt32();
+        ssize_t length = in->readInt32();
+        if (length > 0) {
+            length = in->readInt32();
+            const uint8_t* buf = reinterpret_cast<const uint8_t*>(in->readInplace(length));
+            if (buf == nullptr) {
+                LOG(ERROR) << "readInplace returned null buffer for length " << length;
+                return BAD_VALUE;
+            }
+            payload_.resize(length);
+            std::copy(buf, buf + length, payload_.data());
+        }
+    } else if (response_code_ == ResponseCode::RETRY) {
+        timeout_ = in->readInt32();
+    }
+    return NO_ERROR;
+}
+status_t GateKeeperResponse::writeToParcel(Parcel* out) const {
+    if (out == nullptr) {
+        LOG(ERROR) << "writeToParcel got null out parameter";
+        return BAD_VALUE;
+    }
+    out->writeInt32(int32_t(response_code_));
+    if (response_code_ == ResponseCode::OK) {
+        out->writeInt32(should_reenroll_);
+        out->writeInt32(payload_.size());
+        if (payload_.size() != 0) {
+            out->writeInt32(payload_.size());
+            uint8_t* buf = reinterpret_cast<uint8_t*>(out->writeInplace(payload_.size()));
+            if (buf == nullptr) {
+                LOG(ERROR) << "writeInplace returned null buffer for length " << payload_.size();
+                return BAD_VALUE;
+            }
+            std::copy(payload_.begin(), payload_.end(), buf);
+        }
+    } else if (response_code_ == ResponseCode::RETRY) {
+        out->writeInt32(timeout_);
+    }
+    return NO_ERROR;
+}
+
+}  // namespace gatekeeper
+}  // namespace service
+}  // namespace android
diff --git a/gatekeeperd/IGateKeeperService.cpp b/gatekeeperd/IGateKeeperService.cpp
deleted file mode 100644
index 43d5708..0000000
--- a/gatekeeperd/IGateKeeperService.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright 2015, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-#define LOG_TAG "GateKeeperService"
-#include <utils/Log.h>
-
-#include "IGateKeeperService.h"
-
-namespace android {
-
-const android::String16 IGateKeeperService::descriptor("android.service.gatekeeper.IGateKeeperService");
-const android::String16& IGateKeeperService::getInterfaceDescriptor() const {
-    return IGateKeeperService::descriptor;
-}
-
-status_t BnGateKeeperService::onTransact(
-    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-    switch(code) {
-        case ENROLL: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-
-            ssize_t currentPasswordHandleSize = data.readInt32();
-            const uint8_t *currentPasswordHandle =
-                    static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
-            if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
-            ssize_t currentPasswordSize = data.readInt32();
-            const uint8_t *currentPassword =
-                    static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
-            if (!currentPassword) currentPasswordSize = 0;
-
-            ssize_t desiredPasswordSize = data.readInt32();
-            const uint8_t *desiredPassword =
-                    static_cast<const uint8_t *>(data.readInplace(desiredPasswordSize));
-            if (!desiredPassword) desiredPasswordSize = 0;
-
-            uint8_t *out = NULL;
-            uint32_t outSize = 0;
-            int ret = enroll(uid, currentPasswordHandle, currentPasswordHandleSize,
-                    currentPassword, currentPasswordSize, desiredPassword,
-                    desiredPasswordSize, &out, &outSize);
-
-            reply->writeNoException();
-            reply->writeInt32(1);
-            if (ret == 0 && outSize > 0 && out != NULL) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_OK);
-                reply->writeInt32(0);
-                reply->writeInt32(outSize);
-                reply->writeInt32(outSize);
-                void *buf = reply->writeInplace(outSize);
-                memcpy(buf, out, outSize);
-                delete[] out;
-            } else if (ret > 0) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
-                reply->writeInt32(ret);
-            } else {
-                reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
-            }
-            return OK;
-        }
-        case VERIFY: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-            ssize_t currentPasswordHandleSize = data.readInt32();
-            const uint8_t *currentPasswordHandle =
-                    static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
-            if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
-            ssize_t currentPasswordSize = data.readInt32();
-            const uint8_t *currentPassword =
-                static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
-            if (!currentPassword) currentPasswordSize = 0;
-
-            bool request_reenroll = false;
-            int ret = verify(uid, (uint8_t *) currentPasswordHandle,
-                    currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize,
-                    &request_reenroll);
-
-            reply->writeNoException();
-            reply->writeInt32(1);
-            if (ret == 0) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_OK);
-                reply->writeInt32(request_reenroll ? 1 : 0);
-                reply->writeInt32(0); // no payload returned from this call
-            } else if (ret > 0) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
-                reply->writeInt32(ret);
-            } else {
-                reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
-            }
-            return OK;
-        }
-        case VERIFY_CHALLENGE: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-            uint64_t challenge = data.readInt64();
-            ssize_t currentPasswordHandleSize = data.readInt32();
-            const uint8_t *currentPasswordHandle =
-                    static_cast<const uint8_t *>(data.readInplace(currentPasswordHandleSize));
-            if (!currentPasswordHandle) currentPasswordHandleSize = 0;
-
-            ssize_t currentPasswordSize = data.readInt32();
-            const uint8_t *currentPassword =
-                static_cast<const uint8_t *>(data.readInplace(currentPasswordSize));
-            if (!currentPassword) currentPasswordSize = 0;
-
-
-            uint8_t *out = NULL;
-            uint32_t outSize = 0;
-            bool request_reenroll = false;
-            int ret = verifyChallenge(uid, challenge, (uint8_t *) currentPasswordHandle,
-                    currentPasswordHandleSize, (uint8_t *) currentPassword, currentPasswordSize,
-                    &out, &outSize, &request_reenroll);
-            reply->writeNoException();
-            reply->writeInt32(1);
-            if (ret == 0 && outSize > 0 && out != NULL) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_OK);
-                reply->writeInt32(request_reenroll ? 1 : 0);
-                reply->writeInt32(outSize);
-                reply->writeInt32(outSize);
-                void *buf = reply->writeInplace(outSize);
-                memcpy(buf, out, outSize);
-                delete[] out;
-            } else if (ret > 0) {
-                reply->writeInt32(GATEKEEPER_RESPONSE_RETRY);
-                reply->writeInt32(ret);
-            } else {
-                reply->writeInt32(GATEKEEPER_RESPONSE_ERROR);
-            }
-            return OK;
-        }
-        case GET_SECURE_USER_ID: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-            uint64_t sid = getSecureUserId(uid);
-            reply->writeNoException();
-            reply->writeInt64(sid);
-            return OK;
-        }
-        case CLEAR_SECURE_USER_ID: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            uint32_t uid = data.readInt32();
-            clearSecureUserId(uid);
-            reply->writeNoException();
-            return OK;
-        }
-        case REPORT_DEVICE_SETUP_COMPLETE: {
-            CHECK_INTERFACE(IGateKeeperService, data, reply);
-            reportDeviceSetupComplete();
-            reply->writeNoException();
-            return OK;
-        }
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-};
-
-
-}; // namespace android
diff --git a/gatekeeperd/IGateKeeperService.h b/gatekeeperd/IGateKeeperService.h
deleted file mode 100644
index 2816efc..0000000
--- a/gatekeeperd/IGateKeeperService.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef IGATEKEEPER_SERVICE_H_
-#define IGATEKEEPER_SERVICE_H_
-
-#include <binder/IInterface.h>
-#include <binder/Parcel.h>
-
-namespace android {
-
-/*
- * This must be kept manually in sync with frameworks/base's IGateKeeperService.aidl
- */
-class IGateKeeperService : public IInterface {
-public:
-    enum {
-        ENROLL = IBinder::FIRST_CALL_TRANSACTION + 0,
-        VERIFY = IBinder::FIRST_CALL_TRANSACTION + 1,
-        VERIFY_CHALLENGE = IBinder::FIRST_CALL_TRANSACTION + 2,
-        GET_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 3,
-        CLEAR_SECURE_USER_ID = IBinder::FIRST_CALL_TRANSACTION + 4,
-        REPORT_DEVICE_SETUP_COMPLETE = IBinder::FIRST_CALL_TRANSACTION + 5,
-    };
-
-    enum {
-        GATEKEEPER_RESPONSE_OK = 0,
-        GATEKEEPER_RESPONSE_RETRY = 1,
-        GATEKEEPER_RESPONSE_ERROR = -1,
-    };
-
-    // DECLARE_META_INTERFACE - C++ client interface not needed
-    static const android::String16 descriptor;
-    virtual const android::String16& getInterfaceDescriptor() const;
-    IGateKeeperService() {}
-    virtual ~IGateKeeperService() {}
-
-    /**
-     * Enrolls a password with the GateKeeper. Returns 0 on success, negative on failure.
-     * Returns:
-     * - 0 on success
-     * - A timestamp T > 0 if the call has failed due to throttling and should not
-     *   be reattempted until T milliseconds have elapsed
-     * - -1 on failure
-     */
-    virtual int enroll(uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) = 0;
-
-    /**
-     * Verifies a password previously enrolled with the GateKeeper.
-     * Returns:
-     * - 0 on success
-     * - A timestamp T > 0 if the call has failed due to throttling and should not
-     *   be reattempted until T milliseconds have elapsed
-     * - -1 on failure
-     */
-    virtual int verify(uint32_t uid, const uint8_t *enrolled_password_handle,
-            uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            bool *request_reenroll) = 0;
-
-    /**
-     * Verifies a password previously enrolled with the GateKeeper.
-     * Returns:
-     * - 0 on success
-     * - A timestamp T > 0 if the call has failed due to throttling and should not
-     *   be reattempted until T milliseconds have elapsed
-     * - -1 on failure
-     */
-    virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) = 0;
-    /**
-     * Returns the secure user ID for the provided android user
-     */
-    virtual uint64_t getSecureUserId(uint32_t uid) = 0;
-
-    /**
-     * Clears the secure user ID associated with the user.
-     */
-    virtual void clearSecureUserId(uint32_t uid) = 0;
-
-    /**
-     * Notifies gatekeeper that device setup has been completed and any potentially still existing
-     * state from before a factory reset can be cleaned up (if it has not been already).
-     */
-    virtual void reportDeviceSetupComplete() = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnGateKeeperService: public BnInterface<IGateKeeperService> {
-public:
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-            uint32_t flags = 0);
-};
-
-} // namespace android
-
-#endif
-
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
deleted file mode 100644
index 2f4f4d7..0000000
--- a/gatekeeperd/SoftGateKeeper.h
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#ifndef SOFT_GATEKEEPER_H_
-#define SOFT_GATEKEEPER_H_
-
-extern "C" {
-#include <openssl/rand.h>
-#include <openssl/sha.h>
-
-#include <crypto_scrypt.h>
-}
-
-#include <android-base/memory.h>
-#include <gatekeeper/gatekeeper.h>
-
-#include <iostream>
-#include <unordered_map>
-#include <memory>
-
-namespace gatekeeper {
-
-struct fast_hash_t {
-    uint64_t salt;
-    uint8_t digest[SHA256_DIGEST_LENGTH];
-};
-
-class SoftGateKeeper : public GateKeeper {
-public:
-    static const uint32_t SIGNATURE_LENGTH_BYTES = 32;
-
-    // scrypt params
-    static const uint64_t N = 16384;
-    static const uint32_t r = 8;
-    static const uint32_t p = 1;
-
-    static const int MAX_UINT_32_CHARS = 11;
-
-    SoftGateKeeper() {
-        key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]);
-        memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES);
-    }
-
-    virtual ~SoftGateKeeper() {
-    }
-
-    virtual bool GetAuthTokenKey(const uint8_t **auth_token_key,
-            uint32_t *length) const {
-        if (auth_token_key == NULL || length == NULL) return false;
-        uint8_t *auth_token_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
-        memcpy(auth_token_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
-
-        *auth_token_key = auth_token_key_copy;
-        *length = SIGNATURE_LENGTH_BYTES;
-        return true;
-    }
-
-    virtual void GetPasswordKey(const uint8_t **password_key, uint32_t *length) {
-        if (password_key == NULL || length == NULL) return;
-        uint8_t *password_key_copy = new uint8_t[SIGNATURE_LENGTH_BYTES];
-        memcpy(password_key_copy, key_.get(), SIGNATURE_LENGTH_BYTES);
-
-        *password_key = password_key_copy;
-        *length = SIGNATURE_LENGTH_BYTES;
-    }
-
-    virtual void ComputePasswordSignature(uint8_t *signature, uint32_t signature_length,
-            const uint8_t *, uint32_t, const uint8_t *password,
-            uint32_t password_length, salt_t salt) const {
-        if (signature == NULL) return;
-        crypto_scrypt(password, password_length, reinterpret_cast<uint8_t *>(&salt),
-                sizeof(salt), N, r, p, signature, signature_length);
-    }
-
-    virtual void GetRandom(void *random, uint32_t requested_length) const {
-        if (random == NULL) return;
-        RAND_pseudo_bytes((uint8_t *) random, requested_length);
-    }
-
-    virtual void ComputeSignature(uint8_t *signature, uint32_t signature_length,
-            const uint8_t *, uint32_t, const uint8_t *, const uint32_t) const {
-        if (signature == NULL) return;
-        memset(signature, 0, signature_length);
-    }
-
-    virtual uint64_t GetMillisecondsSinceBoot() const {
-        struct timespec time;
-        int res = clock_gettime(CLOCK_BOOTTIME, &time);
-        if (res < 0) return 0;
-        return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000);
-    }
-
-    virtual bool IsHardwareBacked() const {
-        return false;
-    }
-
-    virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t *record,
-            bool /* secure */) {
-        failure_record_t *stored = &failure_map_[uid];
-        if (user_id != stored->secure_user_id) {
-            stored->secure_user_id = user_id;
-            stored->last_checked_timestamp = 0;
-            stored->failure_counter = 0;
-        }
-        memcpy(record, stored, sizeof(*record));
-        return true;
-    }
-
-    virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) {
-        failure_record_t *stored = &failure_map_[uid];
-        stored->secure_user_id = user_id;
-        stored->last_checked_timestamp = 0;
-        stored->failure_counter = 0;
-        return true;
-    }
-
-    virtual bool WriteFailureRecord(uint32_t uid, failure_record_t *record, bool /* secure */) {
-        failure_map_[uid] = *record;
-        return true;
-    }
-
-    fast_hash_t ComputeFastHash(const SizedBuffer &password, uint64_t salt) {
-        fast_hash_t fast_hash;
-        size_t digest_size = password.length + sizeof(salt);
-        std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]);
-        memcpy(digest.get(), &salt, sizeof(salt));
-        memcpy(digest.get() + sizeof(salt), password.buffer.get(), password.length);
-
-        SHA256(digest.get(), digest_size, (uint8_t *) &fast_hash.digest);
-
-        fast_hash.salt = salt;
-        return fast_hash;
-    }
-
-    bool VerifyFast(const fast_hash_t &fast_hash, const SizedBuffer &password) {
-        fast_hash_t computed = ComputeFastHash(password, fast_hash.salt);
-        return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0;
-    }
-
-    bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
-        uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id);
-        FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
-        if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
-            return true;
-        } else {
-            if (GateKeeper::DoVerify(expected_handle, password)) {
-                uint64_t salt;
-                GetRandom(&salt, sizeof(salt));
-                fast_hash_map_[user_id] = ComputeFastHash(password, salt);
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-private:
-
-    typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
-    typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
-
-    std::unique_ptr<uint8_t[]> key_;
-    FailureRecordMap failure_map_;
-    FastHashMap fast_hash_map_;
-};
-}
-
-#endif // SOFT_GATEKEEPER_H_
diff --git a/gatekeeperd/SoftGateKeeperDevice.cpp b/gatekeeperd/SoftGateKeeperDevice.cpp
deleted file mode 100644
index f5e2ce6..0000000
--- a/gatekeeperd/SoftGateKeeperDevice.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "SoftGateKeeper.h"
-#include "SoftGateKeeperDevice.h"
-
-namespace android {
-
-int SoftGateKeeperDevice::enroll(uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
-
-    if (enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
-            desired_password == NULL || desired_password_length == 0)
-        return -EINVAL;
-
-    // Current password and current password handle go together
-    if (current_password_handle == NULL || current_password_handle_length == 0 ||
-            current_password == NULL || current_password_length == 0) {
-        current_password_handle = NULL;
-        current_password_handle_length = 0;
-        current_password = NULL;
-        current_password_length = 0;
-    }
-
-    SizedBuffer desired_password_buffer(desired_password_length);
-    memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
-
-    SizedBuffer current_password_handle_buffer(current_password_handle_length);
-    if (current_password_handle) {
-        memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
-                current_password_handle_length);
-    }
-
-    SizedBuffer current_password_buffer(current_password_length);
-    if (current_password) {
-        memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
-    }
-
-    EnrollRequest request(uid, &current_password_handle_buffer, &desired_password_buffer,
-            &current_password_buffer);
-    EnrollResponse response;
-
-    impl_->Enroll(request, &response);
-
-    if (response.error == ERROR_RETRY) {
-        return response.retry_timeout;
-    } else if (response.error != ERROR_NONE) {
-        return -EINVAL;
-    }
-
-    *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
-    *enrolled_password_handle_length = response.enrolled_password_handle.length;
-    return 0;
-}
-
-int SoftGateKeeperDevice::verify(uint32_t uid,
-        uint64_t challenge, const uint8_t *enrolled_password_handle,
-        uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
-        uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
-        bool *request_reenroll) {
-
-    if (enrolled_password_handle == NULL ||
-            provided_password == NULL) {
-        return -EINVAL;
-    }
-
-    SizedBuffer password_handle_buffer(enrolled_password_handle_length);
-    memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
-            enrolled_password_handle_length);
-    SizedBuffer provided_password_buffer(provided_password_length);
-    memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
-
-    VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
-    VerifyResponse response;
-
-    impl_->Verify(request, &response);
-
-    if (response.error == ERROR_RETRY) {
-        return response.retry_timeout;
-    } else if (response.error != ERROR_NONE) {
-        return -EINVAL;
-    }
-
-    if (auth_token != NULL && auth_token_length != NULL) {
-       *auth_token = response.auth_token.buffer.release();
-       *auth_token_length = response.auth_token.length;
-    }
-
-    if (request_reenroll != NULL) {
-        *request_reenroll = response.request_reenroll;
-    }
-
-    return 0;
-}
-} // namespace android
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
deleted file mode 100644
index e3dc068..0000000
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SOFT_GATEKEEPER_DEVICE_H_
-#define SOFT_GATEKEEPER_DEVICE_H_
-
-#include "SoftGateKeeper.h"
-
-#include <memory>
-
-using namespace gatekeeper;
-
-namespace android {
-
-/**
- * Software based GateKeeper implementation
- */
-class SoftGateKeeperDevice {
-public:
-    SoftGateKeeperDevice() {
-        impl_.reset(new SoftGateKeeper());
-    }
-
-   // Wrappers to translate the gatekeeper HAL API to the Kegyuard Messages API.
-
-    /**
-     * Enrolls password_payload, which should be derived from a user selected pin or password,
-     * with the authentication factor private key used only for enrolling authentication
-     * factor data.
-     *
-     * Returns: 0 on success or an error code less than 0 on error.
-     * On error, enrolled_password_handle will not be allocated.
-     */
-    int enroll(uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
-
-    /**
-     * Verifies provided_password matches enrolled_password_handle.
-     *
-     * Implementations of this module may retain the result of this call
-     * to attest to the recency of authentication.
-     *
-     * On success, writes the address of a verification token to auth_token,
-     * usable to attest password verification to other trusted services. Clients
-     * may pass NULL for this value.
-     *
-     * Returns: 0 on success or an error code less than 0 on error
-     * On error, verification token will not be allocated
-     */
-    int verify(uint32_t uid, uint64_t challenge,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
-private:
-    std::unique_ptr<SoftGateKeeper> impl_;
-};
-
-} // namespace gatekeeper
-
-#endif //SOFT_GATEKEEPER_DEVICE_H_
diff --git a/logd/LogBufferInterface.cpp b/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
similarity index 60%
rename from logd/LogBufferInterface.cpp
rename to gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
index 4b6d363..097bb54 100644
--- a/logd/LogBufferInterface.cpp
+++ b/gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,11 @@
  * limitations under the License.
  */
 
-#include "LogBufferInterface.h"
-#include "LogUtils.h"
+package android.service.gatekeeper;
 
-LogBufferInterface::LogBufferInterface() {
-}
-LogBufferInterface::~LogBufferInterface() {
-}
-uid_t LogBufferInterface::pidToUid(pid_t pid) {
-    return android::pidToUid(pid);
-}
-pid_t LogBufferInterface::tidToPid(pid_t tid) {
-    return android::tidToPid(tid);
-}
+/**
+ * Response object for a GateKeeper verification request.
+ * @hide
+ */
+parcelable GateKeeperResponse cpp_header "gatekeeper/GateKeeperResponse.h";
+
diff --git a/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
new file mode 100644
index 0000000..57adaba
--- /dev/null
+++ b/gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.gatekeeper;
+
+import android.service.gatekeeper.GateKeeperResponse;
+
+/**
+ * Interface for communication with GateKeeper, the
+ * secure password storage daemon.
+ *
+ * This must be kept manually in sync with system/core/gatekeeperd
+ * until AIDL can generate both C++ and Java bindings.
+ *
+ * @hide
+ */
+interface IGateKeeperService {
+    /**
+     * Enrolls a password, returning the handle to the enrollment to be stored locally.
+     * @param uid The Android user ID associated to this enrollment
+     * @param currentPasswordHandle The previously enrolled handle, or null if none
+     * @param currentPassword The previously enrolled plaintext password, or null if none.
+     *                        If provided, must verify against the currentPasswordHandle.
+     * @param desiredPassword The new desired password, for which a handle will be returned
+     *                        upon success.
+     * @return an EnrollResponse or null on failure
+     */
+    GateKeeperResponse enroll(int uid, in @nullable byte[] currentPasswordHandle,
+            in @nullable byte[] currentPassword, in byte[] desiredPassword);
+
+    /**
+     * Verifies an enrolled handle against a provided, plaintext blob.
+     * @param uid The Android user ID associated to this enrollment
+     * @param enrolledPasswordHandle The handle against which the provided password will be
+     *                               verified.
+     * @param The plaintext blob to verify against enrolledPassword.
+     * @return a VerifyResponse, or null on failure.
+     */
+    GateKeeperResponse verify(int uid, in byte[] enrolledPasswordHandle, in byte[] providedPassword);
+
+    /**
+     * Verifies an enrolled handle against a provided, plaintext blob.
+     * @param uid The Android user ID associated to this enrollment
+     * @param challenge a challenge to authenticate agaisnt the device credential. If successful
+     *                  authentication occurs, this value will be written to the returned
+     *                  authentication attestation.
+     * @param enrolledPasswordHandle The handle against which the provided password will be
+     *                               verified.
+     * @param The plaintext blob to verify against enrolledPassword.
+     * @return a VerifyResponse with an attestation, or null on failure.
+     */
+    GateKeeperResponse verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
+            in byte[] providedPassword);
+
+    /**
+     * Retrieves the secure identifier for the user with the provided Android ID,
+     * or 0 if none is found.
+     * @param uid the Android user id
+     */
+    long getSecureUserId(int uid);
+
+    /**
+     * Clears secure user id associated with the provided Android ID.
+     * Must be called when password is set to NONE.
+     * @param uid the Android user id.
+     */
+    void clearSecureUserId(int uid);
+
+    /**
+     * Notifies gatekeeper that device setup has been completed and any potentially still existing
+     * state from before a factory reset can be cleaned up (if it has not been already).
+     */
+    void reportDeviceSetupComplete();
+}
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 8700c34..1d65b1c 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -16,7 +16,8 @@
 
 #define LOG_TAG "gatekeeperd"
 
-#include "IGateKeeperService.h"
+#include <android/service/gatekeeper/BnGateKeeperService.h>
+#include <gatekeeper/GateKeeperResponse.h>
 
 #include <errno.h>
 #include <fcntl.h>
@@ -41,8 +42,6 @@
 #include <utils/Log.h>
 #include <utils/String16.h>
 
-#include "SoftGateKeeperDevice.h"
-
 #include <hidl/HidlSupport.h>
 #include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
 
@@ -52,6 +51,11 @@
 using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
 using android::hardware::Return;
 
+using ::android::binder::Status;
+using ::android::service::gatekeeper::BnGateKeeperService;
+using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
+using GKResponseCode = ::android::service::gatekeeper::ResponseCode;
+
 namespace android {
 
 static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE");
@@ -64,9 +68,8 @@
         hw_device = IGatekeeper::getService();
         is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
 
-        if (hw_device == nullptr) {
-            ALOGW("falling back to software GateKeeper");
-            soft_device.reset(new SoftGateKeeperDevice());
+        if (!hw_device) {
+            LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
         }
     }
 
@@ -92,7 +95,7 @@
 
         if (mark_cold_boot() && !is_running_gsi) {
             ALOGI("cold boot: clearing state");
-            if (hw_device != nullptr) {
+            if (hw_device) {
                 hw_device->deleteAllUsers([](const GatekeeperResponse &){});
             }
         }
@@ -154,16 +157,16 @@
         return uid;
     }
 
-    virtual int enroll(uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
+
+    Status enroll(int32_t uid, const std::unique_ptr<std::vector<uint8_t>>& currentPasswordHandle,
+                  const std::unique_ptr<std::vector<uint8_t>>& currentPassword,
+                  const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override {
         IPCThreadState* ipc = IPCThreadState::self();
         const int calling_pid = ipc->getCallingPid();
         const int calling_uid = ipc->getCallingUid();
         if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
-            return PERMISSION_DENIED;
+            return GK_ERROR;
         }
 
         // Make sure to clear any state from before factory reset as soon as a credential is
@@ -171,225 +174,189 @@
         clear_state_if_needed();
 
         // need a desired password to enroll
-        if (desired_password_length == 0) return -EINVAL;
+        if (desiredPassword.size() == 0) return GK_ERROR;
 
-        int ret;
-        if (hw_device != nullptr) {
-            const gatekeeper::password_handle_t *handle =
-                    reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle);
+        if (!hw_device) {
+            LOG(ERROR) << "has no HAL to talk to";
+            return GK_ERROR;
+        }
 
-            if (handle != NULL && handle->version != 0 && !handle->hardware_backed) {
-                // handle is being re-enrolled from a software version. HAL probably won't accept
-                // the handle as valid, so we nullify it and enroll from scratch
-                current_password_handle = NULL;
-                current_password_handle_length = 0;
-                current_password = NULL;
-                current_password_length = 0;
+        android::hardware::hidl_vec<uint8_t> curPwdHandle;
+        android::hardware::hidl_vec<uint8_t> curPwd;
+
+        if (currentPasswordHandle && currentPassword) {
+            if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
+                LOG(INFO) << "Password handle has wrong length";
+                return GK_ERROR;
             }
+            curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),
+                                       currentPasswordHandle->size());
+            curPwd.setToExternal(const_cast<uint8_t*>(currentPassword->data()),
+                                 currentPassword->size());
+        }
 
-            android::hardware::hidl_vec<uint8_t> curPwdHandle;
-            curPwdHandle.setToExternal(const_cast<uint8_t*>(current_password_handle),
-                                       current_password_handle_length);
-            android::hardware::hidl_vec<uint8_t> curPwd;
-            curPwd.setToExternal(const_cast<uint8_t*>(current_password),
-                                 current_password_length);
-            android::hardware::hidl_vec<uint8_t> newPwd;
-            newPwd.setToExternal(const_cast<uint8_t*>(desired_password),
-                                 desired_password_length);
+        android::hardware::hidl_vec<uint8_t> newPwd;
+        newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
 
-            uint32_t hw_uid = adjust_uid(uid);
-            Return<void> hwRes = hw_device->enroll(hw_uid, curPwdHandle, curPwd, newPwd,
-                              [&ret, enrolled_password_handle, enrolled_password_handle_length]
-                                   (const GatekeeperResponse &rsp) {
-                ret = static_cast<int>(rsp.code); // propagate errors
-                if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
-                    if (enrolled_password_handle != nullptr &&
-                        enrolled_password_handle_length != nullptr) {
-                        *enrolled_password_handle = new uint8_t[rsp.data.size()];
-                        *enrolled_password_handle_length = rsp.data.size();
-                        memcpy(*enrolled_password_handle, rsp.data.data(),
-                               *enrolled_password_handle_length);
+        uint32_t hw_uid = adjust_uid(uid);
+        Return<void> hwRes = hw_device->enroll(
+                hw_uid, curPwdHandle, curPwd, newPwd, [&gkResponse](const GatekeeperResponse& rsp) {
+                    if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+                        *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
+                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
+                               rsp.timeout > 0) {
+                        *gkResponse = GKResponse::retry(rsp.timeout);
+                    } else {
+                        *gkResponse = GKResponse::error();
                     }
-                    ret = 0; // all success states are reported as 0
-                } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
-                    ret = rsp.timeout;
-                }
-            });
-            if (!hwRes.isOk()) {
-                ALOGE("enroll transaction failed\n");
-                ret = -1;
+                });
+        if (!hwRes.isOk()) {
+            LOG(ERROR) << "enroll transaction failed";
+            return GK_ERROR;
+        }
+
+        if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
+            if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
+                LOG(ERROR) << "HAL returned password handle of invalid length "
+                           << gkResponse->payload().size();
+                return GK_ERROR;
             }
-        } else {
-            ret = soft_device->enroll(uid,
-                    current_password_handle, current_password_handle_length,
-                    current_password, current_password_length,
-                    desired_password, desired_password_length,
-                    enrolled_password_handle, enrolled_password_handle_length);
-        }
 
-        if (ret == GATEKEEPER_RESPONSE_OK && (*enrolled_password_handle == nullptr ||
-            *enrolled_password_handle_length != sizeof(password_handle_t))) {
-            ret = GATEKEEPER_RESPONSE_ERROR;
-            ALOGE("HAL: password_handle=%p size_of_handle=%" PRIu32 "\n",
-                  *enrolled_password_handle, *enrolled_password_handle_length);
-        }
-
-        if (ret == GATEKEEPER_RESPONSE_OK) {
-            gatekeeper::password_handle_t *handle =
-                    reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
+            const gatekeeper::password_handle_t* handle =
+                    reinterpret_cast<const gatekeeper::password_handle_t*>(
+                            gkResponse->payload().data());
             store_sid(uid, handle->user_id);
-            bool rr;
 
+            GKResponse verifyResponse;
             // immediately verify this password so we don't ask the user to enter it again
             // if they just created it.
-            verify(uid, *enrolled_password_handle, sizeof(password_handle_t), desired_password,
-                    desired_password_length, &rr);
+            auto status = verify(uid, gkResponse->payload(), desiredPassword, &verifyResponse);
+            if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
+                LOG(ERROR) << "Failed to verify password after enrolling";
+            }
         }
 
-        return ret;
+        return Status::ok();
     }
 
-    virtual int verify(uint32_t uid,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length, bool *request_reenroll) {
-        uint8_t *auth_token = nullptr;
-        uint32_t auth_token_length;
-        int ret = verifyChallenge(uid, 0, enrolled_password_handle, enrolled_password_handle_length,
-                provided_password, provided_password_length,
-                &auth_token, &auth_token_length, request_reenroll);
-        delete [] auth_token;
-        return ret;
+    Status verify(int32_t uid, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+                  const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override {
+        return verifyChallenge(uid, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
+                               gkResponse);
     }
 
-    virtual int verifyChallenge(uint32_t uid, uint64_t challenge,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+    Status verifyChallenge(int32_t uid, int64_t challenge,
+                           const std::vector<uint8_t>& enrolledPasswordHandle,
+                           const std::vector<uint8_t>& providedPassword,
+                           GKResponse* gkResponse) override {
         IPCThreadState* ipc = IPCThreadState::self();
         const int calling_pid = ipc->getCallingPid();
         const int calling_uid = ipc->getCallingUid();
         if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
-            return PERMISSION_DENIED;
+            return GK_ERROR;
         }
 
         // can't verify if we're missing either param
-        if ((enrolled_password_handle_length | provided_password_length) == 0)
-            return -EINVAL;
+        if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
 
-        int ret;
-        if (hw_device != nullptr) {
-            const gatekeeper::password_handle_t *handle =
-                    reinterpret_cast<const gatekeeper::password_handle_t *>(enrolled_password_handle);
-            // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to
-            // a HAL if there was none before
-            if (handle->version == 0 || handle->hardware_backed) {
-                uint32_t hw_uid = adjust_uid(uid);
-                android::hardware::hidl_vec<uint8_t> curPwdHandle;
-                curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolled_password_handle),
-                                           enrolled_password_handle_length);
-                android::hardware::hidl_vec<uint8_t> enteredPwd;
-                enteredPwd.setToExternal(const_cast<uint8_t*>(provided_password),
-                                         provided_password_length);
-                Return<void> hwRes = hw_device->verify(hw_uid, challenge, curPwdHandle, enteredPwd,
-                                        [&ret, request_reenroll, auth_token, auth_token_length]
-                                             (const GatekeeperResponse &rsp) {
-                    ret = static_cast<int>(rsp.code); // propagate errors
-                    if (auth_token != nullptr && auth_token_length != nullptr &&
-                        rsp.code >= GatekeeperStatusCode::STATUS_OK) {
-                        *auth_token = new uint8_t[rsp.data.size()];
-                        *auth_token_length = rsp.data.size();
-                        memcpy(*auth_token, rsp.data.data(), *auth_token_length);
-                        if (request_reenroll != nullptr) {
-                            *request_reenroll = (rsp.code == GatekeeperStatusCode::STATUS_REENROLL);
-                        }
-                        ret = 0; // all success states are reported as 0
-                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&
-                               rsp.timeout > 0) {
-                        ret = rsp.timeout;
+        if (!hw_device) return GK_ERROR;
+
+        if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
+            LOG(INFO) << "Password handle has wrong length";
+            return GK_ERROR;
+        }
+        const gatekeeper::password_handle_t* handle =
+                reinterpret_cast<const gatekeeper::password_handle_t*>(
+                        enrolledPasswordHandle.data());
+
+        uint32_t hw_uid = adjust_uid(uid);
+        android::hardware::hidl_vec<uint8_t> curPwdHandle;
+        curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
+                                   enrolledPasswordHandle.size());
+        android::hardware::hidl_vec<uint8_t> enteredPwd;
+        enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
+                                 providedPassword.size());
+
+        Return<void> hwRes = hw_device->verify(
+                hw_uid, challenge, curPwdHandle, enteredPwd,
+                [&gkResponse](const GatekeeperResponse& rsp) {
+                    if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
+                        *gkResponse = GKResponse::ok(
+                                {rsp.data.begin(), rsp.data.end()},
+                                rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
+                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) {
+                        *gkResponse = GKResponse::retry(rsp.timeout);
+                    } else {
+                        *gkResponse = GKResponse::error();
                     }
                 });
-                if (!hwRes.isOk()) {
-                    ALOGE("verify transaction failed\n");
-                    ret = -1;
-                }
-            } else {
-                // upgrade scenario, a HAL has been added to this device where there was none before
-                SoftGateKeeperDevice soft_dev;
-                ret = soft_dev.verify(uid, challenge,
-                    enrolled_password_handle, enrolled_password_handle_length,
-                    provided_password, provided_password_length, auth_token, auth_token_length,
-                    request_reenroll);
 
-                if (ret == 0) {
-                    // success! re-enroll with HAL
-                    *request_reenroll = true;
+        if (!hwRes.isOk()) {
+            LOG(ERROR) << "verify transaction failed";
+            return GK_ERROR;
+        }
+
+        if (gkResponse->response_code() == GKResponseCode::OK) {
+            if (gkResponse->payload().size() != 0) {
+                sp<IServiceManager> sm = defaultServiceManager();
+                sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+                sp<security::keystore::IKeystoreService> service =
+                        interface_cast<security::keystore::IKeystoreService>(binder);
+
+                if (service) {
+                    int result = 0;
+                    auto binder_result = service->addAuthToken(gkResponse->payload(), &result);
+                    if (!binder_result.isOk() ||
+                        !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+                        LOG(ERROR) << "Failure sending auth token to KeyStore: " << result;
+                    }
+                } else {
+                    LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with Keystore.";
                 }
             }
-        } else {
-            ret = soft_device->verify(uid, challenge,
-                enrolled_password_handle, enrolled_password_handle_length,
-                provided_password, provided_password_length, auth_token, auth_token_length,
-                request_reenroll);
+
+            maybe_store_sid(uid, handle->user_id);
         }
 
-        if (ret == 0 && *auth_token != NULL && *auth_token_length > 0) {
-            // TODO: cache service?
-            sp<IServiceManager> sm = defaultServiceManager();
-            sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
-            sp<security::keystore::IKeystoreService> service =
-                    interface_cast<security::keystore::IKeystoreService>(binder);
-            if (service != NULL) {
-                std::vector<uint8_t> auth_token_vector(*auth_token,
-                                                       (*auth_token) + *auth_token_length);
-                int result = 0;
-                auto binder_result = service->addAuthToken(auth_token_vector, &result);
-                if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
-                    ALOGE("Failure sending auth token to KeyStore: %" PRId32, result);
-                }
-            } else {
-                ALOGE("Unable to communicate with KeyStore");
-            }
-        }
-
-        if (ret == 0) {
-            maybe_store_sid(uid, reinterpret_cast<const gatekeeper::password_handle_t *>(
-                        enrolled_password_handle)->user_id);
-        }
-
-        return ret;
+        return Status::ok();
     }
 
-    virtual uint64_t getSecureUserId(uint32_t uid) { return read_sid(uid); }
+    Status getSecureUserId(int32_t uid, int64_t* sid) override {
+        *sid = read_sid(uid);
+        return Status::ok();
+    }
 
-    virtual void clearSecureUserId(uint32_t uid) {
+    Status clearSecureUserId(int32_t uid) override {
         IPCThreadState* ipc = IPCThreadState::self();
         const int calling_pid = ipc->getCallingPid();
         const int calling_uid = ipc->getCallingUid();
         if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
             ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
-            return;
+            return Status::ok();
         }
         clear_sid(uid);
 
-        if (hw_device != nullptr) {
+        if (hw_device) {
             uint32_t hw_uid = adjust_uid(uid);
             hw_device->deleteUser(hw_uid, [] (const GatekeeperResponse &){});
         }
+        return Status::ok();
     }
 
-    virtual void reportDeviceSetupComplete() {
+    Status reportDeviceSetupComplete() override {
         IPCThreadState* ipc = IPCThreadState::self();
         const int calling_pid = ipc->getCallingPid();
         const int calling_uid = ipc->getCallingUid();
         if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
             ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
-            return;
+            return Status::ok();
         }
 
         clear_state_if_needed();
+        return Status::ok();
     }
 
-    virtual status_t dump(int fd, const Vector<String16> &) {
+    status_t dump(int fd, const Vector<String16>&) override {
         IPCThreadState* ipc = IPCThreadState::self();
         const int pid = ipc->getCallingPid();
         const int uid = ipc->getCallingUid();
@@ -410,7 +377,6 @@
 
 private:
     sp<IGatekeeper> hw_device;
-    std::unique_ptr<SoftGateKeeperDevice> soft_device;
 
     bool clear_state_if_needed_done;
     bool is_running_gsi;
diff --git a/gatekeeperd/include/gatekeeper/GateKeeperResponse.h b/gatekeeperd/include/gatekeeper/GateKeeperResponse.h
new file mode 100644
index 0000000..99fff02
--- /dev/null
+++ b/gatekeeperd/include/gatekeeper/GateKeeperResponse.h
@@ -0,0 +1,85 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
+#define GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
+
+#include <binder/Parcelable.h>
+
+namespace android {
+namespace service {
+namespace gatekeeper {
+
+enum class ResponseCode : int32_t {
+    ERROR = -1,
+    OK = 0,
+    RETRY = 1,
+};
+
+class GateKeeperResponse : public ::android::Parcelable {
+    GateKeeperResponse(ResponseCode response_code, int32_t timeout = 0,
+                       std::vector<uint8_t> payload = {}, bool should_reenroll = false)
+        : response_code_(response_code),
+          timeout_(timeout),
+          payload_(std::move(payload)),
+          should_reenroll_(should_reenroll) {}
+
+  public:
+    GateKeeperResponse() = default;
+    GateKeeperResponse(GateKeeperResponse&&) = default;
+    GateKeeperResponse(const GateKeeperResponse&) = default;
+    GateKeeperResponse& operator=(GateKeeperResponse&&) = default;
+
+    static GateKeeperResponse error() { return GateKeeperResponse(ResponseCode::ERROR); }
+    static GateKeeperResponse retry(int32_t timeout) {
+        return GateKeeperResponse(ResponseCode::RETRY, timeout);
+    }
+    static GateKeeperResponse ok(std::vector<uint8_t> payload, bool reenroll = false) {
+        return GateKeeperResponse(ResponseCode::OK, 0, std::move(payload), reenroll);
+    }
+
+    status_t readFromParcel(const Parcel* in) override;
+    status_t writeToParcel(Parcel* out) const override;
+
+    const std::vector<uint8_t>& payload() const { return payload_; }
+
+    void payload(std::vector<uint8_t> payload) { payload_ = payload; }
+
+    ResponseCode response_code() const { return response_code_; }
+
+    void response_code(ResponseCode response_code) { response_code_ = response_code; }
+
+    bool should_reenroll() const { return should_reenroll_; }
+
+    void should_reenroll(bool should_reenroll) { should_reenroll_ = should_reenroll; }
+
+    int32_t timeout() const { return timeout_; }
+
+    void timeout(int32_t timeout) { timeout_ = timeout; }
+
+  private:
+    ResponseCode response_code_;
+    int32_t timeout_;
+    std::vector<uint8_t> payload_;
+    bool should_reenroll_;
+};
+
+}  // namespace gatekeeper
+}  // namespace service
+}  // namespace android
+
+#endif  // GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_
diff --git a/gatekeeperd/tests/Android.bp b/gatekeeperd/tests/Android.bp
deleted file mode 100644
index d4cf93b..0000000
--- a/gatekeeperd/tests/Android.bp
+++ /dev/null
@@ -1,34 +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.
-//
-
-cc_test {
-    name: "gatekeeperd-unit-tests",
-
-    cflags: [
-        "-g",
-        "-Wall",
-        "-Werror",
-        "-Wno-missing-field-initializers",
-    ],
-    shared_libs: [
-        "libgatekeeper",
-        "libcrypto",
-        "libbase",
-    ],
-    static_libs: ["libscrypt_static"],
-    include_dirs: ["external/scrypt/lib/crypto"],
-    srcs: ["gatekeeper_test.cpp"],
-}
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
deleted file mode 100644
index 100375f..0000000
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <arpa/inet.h>
-#include <iostream>
-
-#include <gtest/gtest.h>
-#include <hardware/hw_auth_token.h>
-
-#include "../SoftGateKeeper.h"
-
-using ::gatekeeper::SizedBuffer;
-using ::testing::Test;
-using ::gatekeeper::EnrollRequest;
-using ::gatekeeper::EnrollResponse;
-using ::gatekeeper::VerifyRequest;
-using ::gatekeeper::VerifyResponse;
-using ::gatekeeper::SoftGateKeeper;
-using ::gatekeeper::secure_id_t;
-
-static void do_enroll(SoftGateKeeper &gatekeeper, EnrollResponse *response) {
-    SizedBuffer password;
-
-    password.buffer.reset(new uint8_t[16]);
-    password.length = 16;
-    memset(password.buffer.get(), 0, 16);
-    EnrollRequest request(0, NULL, &password, NULL);
-
-    gatekeeper.Enroll(request, response);
-}
-
-TEST(GateKeeperTest, EnrollSuccess) {
-    SoftGateKeeper gatekeeper;
-    EnrollResponse response;
-    do_enroll(gatekeeper, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-}
-
-TEST(GateKeeperTest, EnrollBogusData) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer password;
-    EnrollResponse response;
-
-    EnrollRequest request(0, NULL, &password, NULL);
-
-    gatekeeper.Enroll(request, &response);
-
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
-}
-
-TEST(GateKeeperTest, VerifySuccess) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer provided_password;
-    EnrollResponse enroll_response;
-
-    provided_password.buffer.reset(new uint8_t[16]);
-    provided_password.length = 16;
-    memset(provided_password.buffer.get(), 0, 16);
-
-    do_enroll(gatekeeper, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-    VerifyRequest request(0, 1, &enroll_response.enrolled_password_handle,
-            &provided_password);
-    VerifyResponse response;
-
-    gatekeeper.Verify(request, &response);
-
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-
-    hw_auth_token_t *auth_token =
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
-    ASSERT_EQ((uint32_t) HW_AUTH_PASSWORD, ntohl(auth_token->authenticator_type));
-    ASSERT_EQ((uint64_t) 1, auth_token->challenge);
-    ASSERT_NE(~((uint32_t) 0), auth_token->timestamp);
-    ASSERT_NE((uint64_t) 0, auth_token->user_id);
-    ASSERT_NE((uint64_t) 0, auth_token->authenticator_id);
-}
-
-TEST(GateKeeperTest, TrustedReEnroll) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer provided_password;
-    EnrollResponse enroll_response;
-    SizedBuffer password_handle;
-
-    // do_enroll enrolls an all 0 password
-    provided_password.buffer.reset(new uint8_t[16]);
-    provided_password.length = 16;
-    memset(provided_password.buffer.get(), 0, 16);
-    do_enroll(gatekeeper, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
-    // keep a copy of the handle
-    password_handle.buffer.reset(new uint8_t[enroll_response.enrolled_password_handle.length]);
-    password_handle.length = enroll_response.enrolled_password_handle.length;
-    memcpy(password_handle.buffer.get(), enroll_response.enrolled_password_handle.buffer.get(),
-            password_handle.length);
-
-    // verify first password
-    VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle,
-            &provided_password);
-    VerifyResponse response;
-    gatekeeper.Verify(request, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-    hw_auth_token_t *auth_token =
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
-    secure_id_t secure_id = auth_token->user_id;
-
-    // enroll new password
-    provided_password.buffer.reset(new uint8_t[16]);
-    provided_password.length = 16;
-    memset(provided_password.buffer.get(), 0, 16);
-    SizedBuffer password;
-    password.buffer.reset(new uint8_t[16]);
-    memset(password.buffer.get(), 1, 16);
-    password.length = 16;
-    EnrollRequest enroll_request(0, &password_handle, &password, &provided_password);
-    gatekeeper.Enroll(enroll_request, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
-    // verify new password
-    password.buffer.reset(new uint8_t[16]);
-    memset(password.buffer.get(), 1, 16);
-    password.length = 16;
-    VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle,
-            &password);
-    gatekeeper.Verify(new_request, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-    ASSERT_EQ(secure_id,
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id);
-}
-
-
-TEST(GateKeeperTest, UntrustedReEnroll) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer provided_password;
-    EnrollResponse enroll_response;
-
-    // do_enroll enrolls an all 0 password
-    provided_password.buffer.reset(new uint8_t[16]);
-    provided_password.length = 16;
-    memset(provided_password.buffer.get(), 0, 16);
-    do_enroll(gatekeeper, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
-    // verify first password
-    VerifyRequest request(0, 0, &enroll_response.enrolled_password_handle,
-            &provided_password);
-    VerifyResponse response;
-    gatekeeper.Verify(request, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-    hw_auth_token_t *auth_token =
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get());
-
-    secure_id_t secure_id = auth_token->user_id;
-
-    // enroll new password
-    SizedBuffer password;
-    password.buffer.reset(new uint8_t[16]);
-    memset(password.buffer.get(), 1, 16);
-    password.length = 16;
-    EnrollRequest enroll_request(0, NULL, &password, NULL);
-    gatekeeper.Enroll(enroll_request, &enroll_response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, enroll_response.error);
-
-    // verify new password
-    password.buffer.reset(new uint8_t[16]);
-    memset(password.buffer.get(), 1, 16);
-    password.length = 16;
-    VerifyRequest new_request(0, 0, &enroll_response.enrolled_password_handle,
-            &password);
-    gatekeeper.Verify(new_request, &response);
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_NONE, response.error);
-    ASSERT_NE(secure_id,
-        reinterpret_cast<hw_auth_token_t *>(response.auth_token.buffer.get())->user_id);
-}
-
-
-TEST(GateKeeperTest, VerifyBogusData) {
-    SoftGateKeeper gatekeeper;
-    SizedBuffer provided_password;
-    SizedBuffer password_handle;
-    VerifyResponse response;
-
-    VerifyRequest request(0, 0, &provided_password, &password_handle);
-
-    gatekeeper.Verify(request, &response);
-
-    ASSERT_EQ(::gatekeeper::gatekeeper_error_t::ERROR_INVALID, response.error);
-}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 2cf6be9..53be526 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -110,3 +110,55 @@
         "libutils",
     ],
 }
+
+sysprop_library {
+    name: "charger_sysprop",
+    srcs: ["charger.sysprop"],
+    property_owner: "Platform",
+    api_packages: ["android.sysprop"],
+}
+
+cc_library_static {
+    name: "libhealthd_draw",
+    export_include_dirs: ["."],
+    static_libs: [
+        "libcharger_sysprop",
+        "libminui",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    header_libs: ["libbatteryservice_headers"],
+
+    srcs: ["healthd_draw.cpp"],
+}
+
+cc_library_static {
+    name: "libhealthd_charger",
+    local_include_dirs: ["include"],
+    export_include_dirs: [".", "include"],
+
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+        "android.hardware.health@1.0-convert",
+        "libcharger_sysprop",
+        "libhealthstoragedefault",
+        "libhealthd_draw",
+        "libminui",
+    ],
+
+    shared_libs: [
+        "android.hardware.health@2.0",
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libpng",
+        "libsuspend",
+        "libutils",
+    ],
+
+    srcs: [
+        "healthd_mode_charger.cpp",
+        "AnimationParser.cpp",
+    ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index d18f15a..b87f3c7 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -2,74 +2,6 @@
 
 LOCAL_PATH := $(call my-dir)
 
-### libhealthd_draw ###
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libhealthd_draw
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-LOCAL_STATIC_LIBRARIES := libminui
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_SRC_FILES := healthd_draw.cpp
-
-ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_SCREEN),)
-LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=$(TARGET_HEALTHD_DRAW_SPLIT_SCREEN)
-else
-LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_SCREEN=0
-endif
-
-ifneq ($(TARGET_HEALTHD_DRAW_SPLIT_OFFSET),)
-LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=$(TARGET_HEALTHD_DRAW_SPLIT_OFFSET)
-else
-LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
-endif
-
-LOCAL_HEADER_LIBRARIES := libbatteryservice_headers
-
-include $(BUILD_STATIC_LIBRARY)
-
-### libhealthd_charger ###
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS := -Werror
-ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
-LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
-endif
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
-endif
-
-LOCAL_SRC_FILES := \
-    healthd_mode_charger.cpp \
-    AnimationParser.cpp
-
-LOCAL_MODULE := libhealthd_charger
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
-    $(LOCAL_PATH) \
-    $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
-    android.hardware.health@2.0-impl \
-    android.hardware.health@1.0-convert \
-    libhealthstoragedefault \
-    libhealthd_draw \
-    libminui \
-
-LOCAL_SHARED_LIBRARIES := \
-    android.hardware.health@2.0 \
-    libbase \
-    libcutils \
-    liblog \
-    libpng \
-    libutils \
-
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_SHARED_LIBRARIES += libsuspend
-endif
-
-include $(BUILD_STATIC_LIBRARY)
-
 ### charger ###
 include $(CLEAR_VARS)
 ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
@@ -83,18 +15,17 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 
 LOCAL_CFLAGS := -Werror
-ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-LOCAL_CFLAGS += -DCHARGER_NO_UI
-endif
 
 CHARGER_STATIC_LIBRARIES := \
     android.hardware.health@2.0-impl \
     android.hardware.health@1.0-convert \
     libbinderthreadstate \
+    libcharger_sysprop \
     libhidltransport \
     libhidlbase \
     libhwbinder_noltopgo \
     libhealthstoragedefault \
+    libminui \
     libvndksupport \
     libhealthd_charger \
     libhealthd_charger_nops \
@@ -106,18 +37,12 @@
     libbase \
     libcutils \
     libjsoncpp \
+    libpng \
     libprocessgroup \
     liblog \
     libutils \
 
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-CHARGER_STATIC_LIBRARIES += libminui
-CHARGER_SHARED_LIBRARIES += libpng
-endif
-
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
 CHARGER_SHARED_LIBRARIES += libsuspend
-endif
 
 LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
 LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
@@ -141,8 +66,7 @@
 LOCAL_MODULE_STEM := charger
 
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -DCHARGER_NO_UI
+LOCAL_CFLAGS := -Wall -Werror -DCHARGER_FORCE_NO_UI=1
 
 # charger.recovery doesn't link against libhealthd_{charger,draw} or libminui, since it doesn't need
 # any UI support.
@@ -150,6 +74,7 @@
     android.hardware.health@2.0-impl \
     android.hardware.health@1.0-convert \
     libbinderthreadstate \
+    libcharger_sysprop \
     libhidltransport \
     libhidlbase \
     libhwbinder_noltopgo \
@@ -176,7 +101,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := charger_test
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror -DCHARGER_NO_UI
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
 LOCAL_SHARED_LIBRARIES := $(CHARGER_SHARED_LIBRARIES)
 LOCAL_SRC_FILES := \
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
index 864038b..fde3b95 100644
--- a/healthd/AnimationParser.cpp
+++ b/healthd/AnimationParser.cpp
@@ -84,7 +84,6 @@
     static constexpr const char* fail_prefix = "fail: ";
     static constexpr const char* clock_prefix = "clock_display: ";
     static constexpr const char* percent_prefix = "percent_display: ";
-    static constexpr const char* frame_prefix = "frame: ";
 
     std::vector<animation::frame> frames;
 
diff --git a/healthd/api/charger_sysprop-current.txt b/healthd/api/charger_sysprop-current.txt
new file mode 100644
index 0000000..678c847
--- /dev/null
+++ b/healthd/api/charger_sysprop-current.txt
@@ -0,0 +1,29 @@
+props {
+  module: "android.sysprop.ChargerProperties"
+  prop {
+    api_name: "disable_init_blank"
+    scope: Internal
+    prop_name: "ro.charger.disable_init_blank"
+  }
+  prop {
+    api_name: "draw_split_offset"
+    type: Long
+    scope: Internal
+    prop_name: "ro.charger.draw_split_offset"
+  }
+  prop {
+    api_name: "draw_split_screen"
+    scope: Internal
+    prop_name: "ro.charger.draw_split_screen"
+  }
+  prop {
+    api_name: "enable_suspend"
+    scope: Internal
+    prop_name: "ro.charger.enable_suspend"
+  }
+  prop {
+    api_name: "no_ui"
+    scope: Internal
+    prop_name: "ro.charger.no_ui"
+  }
+}
diff --git a/healthd/api/charger_sysprop-latest.txt b/healthd/api/charger_sysprop-latest.txt
new file mode 100644
index 0000000..678c847
--- /dev/null
+++ b/healthd/api/charger_sysprop-latest.txt
@@ -0,0 +1,29 @@
+props {
+  module: "android.sysprop.ChargerProperties"
+  prop {
+    api_name: "disable_init_blank"
+    scope: Internal
+    prop_name: "ro.charger.disable_init_blank"
+  }
+  prop {
+    api_name: "draw_split_offset"
+    type: Long
+    scope: Internal
+    prop_name: "ro.charger.draw_split_offset"
+  }
+  prop {
+    api_name: "draw_split_screen"
+    scope: Internal
+    prop_name: "ro.charger.draw_split_screen"
+  }
+  prop {
+    api_name: "enable_suspend"
+    scope: Internal
+    prop_name: "ro.charger.enable_suspend"
+  }
+  prop {
+    api_name: "no_ui"
+    scope: Internal
+    prop_name: "ro.charger.no_ui"
+  }
+}
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 085cceb..58ed416 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -14,13 +14,18 @@
  * limitations under the License.
  */
 
+#include "charger.sysprop.h"
 #include "healthd_mode_charger.h"
 #include "healthd_mode_charger_nops.h"
 
-int main(int argc, char** argv) {
-#ifdef CHARGER_NO_UI
-    return healthd_charger_nops(argc, argv);
-#else
-    return healthd_charger_main(argc, argv);
+#ifndef CHARGER_FORCE_NO_UI
+#define CHARGER_FORCE_NO_UI 0
 #endif
+
+int main(int argc, char** argv) {
+    if (CHARGER_FORCE_NO_UI || android::sysprop::ChargerProperties::no_ui().value_or(false)) {
+        return healthd_charger_nops(argc, argv);
+    } else {
+        return healthd_charger_main(argc, argv);
+    }
 }
diff --git a/healthd/charger.sysprop b/healthd/charger.sysprop
new file mode 100644
index 0000000..b3f47a1
--- /dev/null
+++ b/healthd/charger.sysprop
@@ -0,0 +1,38 @@
+owner: Platform
+module: "android.sysprop.ChargerProperties"
+
+prop {
+    api_name: "draw_split_screen"
+    type: Boolean
+    prop_name: "ro.charger.draw_split_screen"
+    scope: Internal
+    access: Readonly
+}
+prop {
+    api_name: "draw_split_offset"
+    type: Long
+    prop_name: "ro.charger.draw_split_offset"
+    scope: Internal
+    access: Readonly
+}
+prop {
+    api_name: "disable_init_blank"
+    type: Boolean
+    prop_name: "ro.charger.disable_init_blank"
+    scope: Internal
+    access: Readonly
+}
+prop {
+    api_name: "enable_suspend"
+    type: Boolean
+    prop_name: "ro.charger.enable_suspend"
+    scope: Internal
+    access: Readonly
+}
+prop {
+    api_name: "no_ui"
+    type: Boolean
+    prop_name: "ro.charger.no_ui"
+    scope: Internal
+    access: Readonly
+}
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 3da8bda..50eee19 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -18,15 +18,34 @@
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 
+#include "charger.sysprop.h"
 #include "healthd_draw.h"
 
 #define LOGE(x...) KLOG_ERROR("charger", x);
 #define LOGW(x...) KLOG_WARNING("charger", x);
 #define LOGV(x...) KLOG_DEBUG("charger", x);
 
+static bool get_split_screen() {
+    return android::sysprop::ChargerProperties::draw_split_screen().value_or(false);
+}
+
+static int get_split_offset() {
+    int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0);
+    if (value < static_cast<int64_t>(std::numeric_limits<int>::min())) {
+        LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
+             std::numeric_limits<int>::min());
+        value = std::numeric_limits<int>::min();
+    }
+    if (value > static_cast<int64_t>(std::numeric_limits<int>::max())) {
+        LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
+             std::numeric_limits<int>::max());
+        value = std::numeric_limits<int>::max();
+    }
+    return static_cast<int>(value);
+}
+
 HealthdDraw::HealthdDraw(animation* anim)
-  : kSplitScreen(HEALTHD_DRAW_SPLIT_SCREEN),
-    kSplitOffset(HEALTHD_DRAW_SPLIT_OFFSET) {
+    : kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {
     int ret = gr_init();
 
     if (ret < 0) {
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index edf34f7..d676083 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -43,11 +43,10 @@
 #include <cutils/uevent.h>
 #include <sys/reboot.h>
 
-#ifdef CHARGER_ENABLE_SUSPEND
 #include <suspend/autosuspend.h>
-#endif
 
 #include "AnimationParser.h"
+#include "charger.sysprop.h"
 #include "healthd_draw.h"
 
 #include <health2/Health.h>
@@ -264,18 +263,16 @@
     LOGW("\n");
 }
 
-#ifdef CHARGER_ENABLE_SUSPEND
 static int request_suspend(bool enable) {
+    if (!android::sysprop::ChargerProperties::enable_suspend().value_or(false)) {
+        return 0;
+    }
+
     if (enable)
         return autosuspend_enable();
     else
         return autosuspend_disable();
 }
-#else
-static int request_suspend(bool /*enable*/) {
-    return 0;
-}
-#endif
 
 static void kick_animation(animation* anim) {
     anim->run = true;
@@ -321,10 +318,10 @@
 
         healthd_draw.reset(new HealthdDraw(batt_anim));
 
-#ifndef CHARGER_DISABLE_INIT_BLANK
-        healthd_draw->blank_screen(true);
-        charger->screen_blanked = true;
-#endif
+        if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
+            healthd_draw->blank_screen(true);
+            charger->screen_blanked = true;
+        }
     }
 
     /* animation is over, blank screen and leave */
diff --git a/init/Android.bp b/init/Android.bp
index 6bc581d..37e359c 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -26,6 +26,7 @@
         "-Wextra",
         "-Wno-unused-parameter",
         "-Werror",
+        "-DALLOW_FIRST_STAGE_CONSOLE=0",
         "-DALLOW_LOCAL_PROP_OVERRIDE=0",
         "-DALLOW_PERMISSIVE_SELINUX=0",
         "-DREBOOT_BOOTLOADER_ON_PANIC=0",
@@ -36,6 +37,8 @@
     product_variables: {
         debuggable: {
             cppflags: [
+                "-UALLOW_FIRST_STAGE_CONSOLE",
+                "-DALLOW_FIRST_STAGE_CONSOLE=1",
                 "-UALLOW_LOCAL_PROP_OVERRIDE",
                 "-DALLOW_LOCAL_PROP_OVERRIDE=1",
                 "-UALLOW_PERMISSIVE_SELINUX",
@@ -67,11 +70,11 @@
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
+        "libsnapshot_nobinder",
     ],
     shared_libs: [
         "libbacktrace",
         "libbase",
-        "libbinder",
         "libbootloader_message",
         "libcutils",
         "libcrypto",
@@ -81,6 +84,7 @@
         "libfscrypt",
         "libgsi",
         "libhidl-gen-utils",
+        "libjsoncpp",
         "libkeyutils",
         "liblog",
         "liblogwrap",
@@ -91,12 +95,16 @@
         "libutils",
     ],
     bootstrap: true,
+    visibility: [":__subpackages__"],
 }
 
 cc_library_static {
     name: "libinit",
     recovery_available: true,
-    defaults: ["init_defaults", "selinux_policy_version"],
+    defaults: [
+        "init_defaults",
+        "selinux_policy_version",
+    ],
     srcs: [
         "action.cpp",
         "action_manager.cpp",
@@ -105,14 +113,15 @@
         "bootchart.cpp",
         "builtins.cpp",
         "capabilities.cpp",
-        "descriptors.cpp",
         "devices.cpp",
         "epoll.cpp",
         "firmware_handler.cpp",
         "first_stage_init.cpp",
         "first_stage_mount.cpp",
+        "fscrypt_init_extensions.cpp",
         "import_parser.cpp",
         "init.cpp",
+        "interface_utils.cpp",
         "keychords.cpp",
         "modalias_handler.cpp",
         "mount_handler.cpp",
@@ -142,7 +151,10 @@
         "ueventd_parser.cpp",
         "util.cpp",
     ],
-    whole_static_libs: ["libcap", "com.android.sysprop.apex"],
+    whole_static_libs: [
+        "libcap",
+        "com.android.sysprop.apex",
+    ],
     header_libs: ["bootimg_headers"],
     proto: {
         type: "lite",
@@ -152,11 +164,21 @@
     target: {
         recovery: {
             cflags: ["-DRECOVERY"],
-            exclude_shared_libs: ["libbinder", "libutils"],
+            exclude_shared_libs: [
+                "libbinder",
+                "libutils",
+            ],
         },
     },
 }
 
+phony {
+    name: "init",
+    required: [
+        "init_second_stage",
+    ],
+}
+
 cc_binary {
     name: "init_second_stage",
     recovery_available: true,
@@ -174,7 +196,10 @@
     target: {
         recovery: {
             cflags: ["-DRECOVERY"],
-            exclude_shared_libs: ["libbinder", "libutils"],
+            exclude_shared_libs: [
+                "libbinder",
+                "libutils",
+            ],
         },
     },
 }
@@ -183,9 +208,18 @@
 // ------------------------------------------------------------------------------
 
 cc_test {
-    name: "init_tests",
+    name: "CtsInitTestCases",
     defaults: ["init_defaults"],
-    compile_multilib: "first",
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
     srcs: [
         "devices_test.cpp",
         "init_test.cpp",
@@ -202,7 +236,12 @@
         "util_test.cpp",
     ],
     static_libs: ["libinit"],
-    test_suites: ["device-tests"],
+
+    test_suites: [
+        "cts",
+        "device-tests",
+        "vts",
+    ],
 }
 
 cc_benchmark {
@@ -219,9 +258,13 @@
 
 genrule {
     name: "generated_stub_builtin_function_map",
+    tool_files: ["host_builtin_map.py"],
     out: ["generated_stub_builtin_function_map.h"],
-    srcs: ["builtins.cpp"],
-    cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)",
+    srcs: [
+        "builtins.cpp",
+        "check_builtins.cpp",
+    ],
+    cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
 }
 
 cc_binary {
@@ -240,21 +283,25 @@
     ],
     whole_static_libs: ["libcap"],
     shared_libs: [
-        "libprotobuf-cpp-lite",
-        "libhidl-gen-utils",
-        "libprocessgroup",
-        "liblog",
         "libcutils",
+        "libhidl-gen-utils",
+        "liblog",
+        "libprocessgroup",
+        "libprotobuf-cpp-lite",
+    ],
+    header_libs: [
+        "libjsoncpp_headers",
     ],
     srcs: [
         "action.cpp",
         "action_manager.cpp",
         "action_parser.cpp",
         "capabilities.cpp",
-        "descriptors.cpp",
+        "check_builtins.cpp",
         "epoll.cpp",
         "keychords.cpp",
         "import_parser.cpp",
+        "interface_utils.cpp",
         "host_import_parser.cpp",
         "host_init_verifier.cpp",
         "parser.cpp",
@@ -273,7 +320,7 @@
     },
     generated_headers: [
         "generated_stub_builtin_function_map",
-        "generated_android_ids"
+        "generated_android_ids",
     ],
     target: {
         android: {
@@ -284,5 +331,3 @@
         },
     },
 }
-
-subdirs = ["*"]
diff --git a/init/Android.mk b/init/Android.mk
index b24f757..d7258a7 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -8,6 +8,7 @@
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 init_options += \
+    -DALLOW_FIRST_STAGE_CONSOLE=1 \
     -DALLOW_LOCAL_PROP_OVERRIDE=1 \
     -DALLOW_PERMISSIVE_SELINUX=1 \
     -DREBOOT_BOOTLOADER_ON_PANIC=1 \
@@ -15,6 +16,7 @@
     -DDUMP_ON_UMOUNT_FAILURE=1
 else
 init_options += \
+    -DALLOW_FIRST_STAGE_CONSOLE=0 \
     -DALLOW_LOCAL_PROP_OVERRIDE=0 \
     -DALLOW_PERMISSIVE_SELINUX=0 \
     -DREBOOT_BOOTLOADER_ON_PANIC=0 \
@@ -111,6 +113,8 @@
     libunwindstack \
     libbacktrace \
     libmodprobe \
+    libext2_uuid \
+    libsnapshot_nobinder \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
new file mode 100644
index 0000000..94a02e6
--- /dev/null
+++ b/init/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS init test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="systems" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsInitTestCases" />
+        <option name="runtime-hint" value="65s" />
+    </test>
+</configuration>
diff --git a/init/README.md b/init/README.md
index 8179bff..2de76a9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -300,7 +300,8 @@
 
 `socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
 > Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
-  launched process.  _type_ must be "dgram", "stream" or "seqpacket".  User and
+  launched process.  _type_ must be "dgram", "stream" or "seqpacket".  _type_
+  may end with "+passcred" to enable SO_PASSCRED on the socket. User and
   group default to 0.  'seclabel' is the SELinux security context for the
   socket.  It defaults to the service security context, as specified by
   seclabel or computed based on the service executable file security context.
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
new file mode 100644
index 0000000..c592c37
--- /dev/null
+++ b/init/README.ueventd.md
@@ -0,0 +1,112 @@
+# Ueventd
+-------
+Ueventd manages `/dev`, sets permissions for `/sys`, and handles firmware uevents. It has default
+behavior described below, along with a scripting language that allows customizing this behavior,
+built on the same parser as init.
+
+Ueventd has one generic customization parameter, the size of rcvbuf_size for the ueventd socket. It
+is customized by the `uevent_socket_rcvbuf_size` parameter, which takes the format of
+
+    uevent_socket_rcvbuf_size <size>
+For example
+
+    uevent_socket_rcvbuf_size 16M
+Sets the uevent socket rcvbuf_size to 16 megabytes.
+
+## /dev
+----
+Ueventd listens to the kernel uevent sockets and creates/deletes nodes in `/dev` based on the
+incoming add/remove uevents. It defaults to using `0600` mode and `root` user/group. It always
+creates the nodes with the SELabel from the current loaded SEPolicy. It has three default behaviors
+for the node path:
+
+  1. Block devices are created as `/dev/block/<basename uevent DEVPATH>`. There are symlinks created
+     to this node at `/dev/block/<type>/<parent device>/<basename uevent DEVPATH>`,
+     `/dev/block/<type>/<parent device>/by-name/<uevent PARTNAME>`, and `/dev/block/by-name/<uevent
+     PARTNAME>` if the device is a boot device.
+  2. USB devices are created as `/dev/<uevent DEVNAME>` if `DEVNAME` was specified for the uevent,
+     otherwise as `/dev/bus/usb/<bus_id>/<device_id>` where `bus_id` is `uevent MINOR / 128 + 1` and
+     `device_id` is `uevent MINOR % 128 + 1`.
+  3. All other devices are created as `/dev/<basename uevent DEVPATH>`
+
+The permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These
+lines take the format of
+
+    devname mode uid gid
+For example
+
+    /dev/null 0666 root root
+When `/dev/null` is created, its mode will be set to `0666`, its user to `root` and its group to
+`root`.
+
+The path can be modified using a ueventd.rc script and a `subsystem` section. There are three to set
+for a subsystem: the subsystem name, which device name to use, and which directory to place the
+device in. The section takes the below format of
+
+    subsystem <subsystem_name>
+      devname uevent_devname|uevent_devpath
+      [dirname <directory>]
+
+`subsystem_name` is used to match uevent `SUBSYSTEM` value
+
+`devname` takes one of two options
+  1. `uevent_devname` specifies that the name of the node will be the uevent `DEVNAME`
+  2. `uevent_devpath` specified that the name of the node will be basename uevent `DEVPATH`
+
+`dirname` is an optional parameter that specifies a directory within `/dev` where the node will be
+created.
+
+For example
+
+    subsystem sound
+      devname uevent_devpath
+      dirname /dev/snd
+Indicates that all uevents with `SUBSYSTEM=sound` will create nodes as `/dev/snd/<basename uevent
+DEVPATH>`.
+
+## /sys
+----
+Ueventd by default takes no action for `/sys`, however it can be instructed to set permissions for
+certain files in `/sys` when matching uevents are generated. This is done using a ueventd.rc script
+and a line that begins with `/sys`. These lines take the format of
+
+    nodename attr mode uid gid
+For example
+
+    /sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
+When a uevent that matches the pattern `/sys/devices/system/cpu/cpu*` is sent, the matching sysfs
+attribute, `cpufreq/scaling_max_freq`, will have its mode set to `0664`, its user to to `system` and
+its group set to `system`.
+
+Note that `*` matches as a wildcard and can be used anywhere in a path.
+
+## Firmware loading
+----------------
+Ueventd automatically serves firmware requests by searching through a list of firmware directories
+for a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the
+kernel.
+
+The list of firmware directories is customized by a `firmware_directories` line in a ueventd.rc
+file. This line takes the format of
+
+    firmware_directories <firmware_directory> [ <firmware_directory> ]*
+For example
+
+    firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
+Adds those 4 directories, in that order to the list of firmware directories that will be tried by
+ueventd. Note that this option always accumulates to the list; it is not possible to remove previous
+entries.
+
+Ueventd will wait until after `post-fs` in init, to keep retrying before believing the firmwares are
+not present.
+
+## Coldboot
+--------
+Ueventd must create devices in `/dev` for all devices that have already sent their uevents before
+ueventd has started. To do so, when ueventd is started it does what it calls a 'coldboot' on `/sys`,
+in which it writes 'add' to every 'uevent' file that it finds in `/sys/class`, `/sys/block`, and
+`/sys/devices`. This causes the kernel to regenerate the uevents for these paths, and thus for
+ueventd to create the nodes.
+
+For boot time purposes, this is done in parallel across a set of child processes. `ueventd.cpp` in
+this directory contains documentation on how the parallelization is done.
diff --git a/init/action.cpp b/init/action.cpp
index a169591..69e40d0 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -35,9 +35,11 @@
     builtin_arguments.args.resize(args.size());
     builtin_arguments.args[0] = args[0];
     for (std::size_t i = 1; i < args.size(); ++i) {
-        if (!expand_props(args[i], &builtin_arguments.args[i])) {
-            return Error() << "cannot expand '" << args[i] << "'";
+        auto expanded_arg = ExpandProps(args[i]);
+        if (!expanded_arg) {
+            return expanded_arg.error();
         }
+        builtin_arguments.args[i] = std::move(*expanded_arg);
     }
 
     return function(builtin_arguments);
@@ -66,6 +68,30 @@
     return RunBuiltinFunction(func_, args_, kInitContext);
 }
 
+Result<void> Command::CheckCommand() const {
+    auto builtin_arguments = BuiltinArguments("host_init_verifier");
+
+    builtin_arguments.args.resize(args_.size());
+    builtin_arguments.args[0] = args_[0];
+    for (size_t i = 1; i < args_.size(); ++i) {
+        auto expanded_arg = ExpandProps(args_[i]);
+        if (!expanded_arg) {
+            if (expanded_arg.error().message().find("doesn't exist while expanding") !=
+                std::string::npos) {
+                // If we failed because we won't have a property, use an empty string, which is
+                // never returned from the parser, to indicate that this field cannot be checked.
+                builtin_arguments.args[i] = "";
+            } else {
+                return expanded_arg.error();
+            }
+        } else {
+            builtin_arguments.args[i] = std::move(*expanded_arg);
+        }
+    }
+
+    return func_(builtin_arguments);
+}
+
 std::string Command::BuildCommandString() const {
     return Join(args_, ' ');
 }
@@ -80,28 +106,43 @@
       filename_(filename),
       line_(line) {}
 
-const KeywordFunctionMap* Action::function_map_ = nullptr;
+const BuiltinFunctionMap* Action::function_map_ = nullptr;
 
 Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
     if (!function_map_) {
         return Error() << "no function map available";
     }
 
-    auto function = function_map_->FindFunction(args);
-    if (!function) return Error() << function.error();
+    auto map_result = function_map_->Find(args);
+    if (!map_result) {
+        return Error() << map_result.error();
+    }
 
-    commands_.emplace_back(function->second, function->first, std::move(args), line);
+    commands_.emplace_back(map_result->function, map_result->run_in_subcontext, std::move(args),
+                           line);
     return {};
 }
 
 void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
-    commands_.emplace_back(f, false, std::move(args), line);
+    commands_.emplace_back(std::move(f), false, std::move(args), line);
 }
 
 std::size_t Action::NumCommands() const {
     return commands_.size();
 }
 
+size_t Action::CheckAllCommands() const {
+    size_t failures = 0;
+    for (const auto& command : commands_) {
+        if (auto result = command.CheckCommand(); !result) {
+            LOG(ERROR) << "Command '" << command.BuildCommandString() << "' (" << filename_ << ":"
+                       << command.line() << ") failed: " << result.error();
+            ++failures;
+        }
+    }
+    return failures;
+}
+
 void Action::ExecuteOneCommand(std::size_t command) const {
     // We need a copy here since some Command execution may result in
     // changing commands_ vector by importing .rc files through parser
@@ -120,18 +161,8 @@
     auto result = command.InvokeFunc(subcontext_);
     auto duration = t.duration();
 
-    // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
-    // device, such as '/sys/class/leds/jogball-backlight/brightness'.  As of this writing, there
-    // are 198 such failures on bullhead.  Instead of spamming the log reporting them, we do not
-    // report such failures unless we're running at the DEBUG log level.
-    bool report_failure = !result.has_value();
-    if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
-        result.error().code() == ENOENT) {
-        report_failure = false;
-    }
-
     // Any action longer than 50ms will be warned to user as slow operation
-    if (report_failure || duration > 50ms ||
+    if (!result.has_value() || duration > 50ms ||
         android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
         std::string trigger_name = BuildTriggersString();
         std::string cmd_str = command.BuildCommandString();
@@ -164,10 +195,11 @@
                 found = true;
             }
         } else {
-            std::string prop_val = android::base::GetProperty(trigger_name, "");
-            if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
-                return false;
+            std::string prop_value = android::base::GetProperty(trigger_name, "");
+            if (trigger_value == "*" && !prop_value.empty()) {
+                continue;
             }
+            if (trigger_value != prop_value) return false;
         }
     }
     return found;
diff --git a/init/action.h b/init/action.h
index 13b250a..1534bf9 100644
--- a/init/action.h
+++ b/init/action.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_ACTION_H
-#define _INIT_ACTION_H
+#pragma once
 
 #include <map>
 #include <queue>
@@ -41,6 +40,7 @@
 
     Result<void> InvokeFunc(Subcontext* subcontext) const;
     std::string BuildCommandString() const;
+    Result<void> CheckCommand() const;
 
     int line() const { return line_; }
 
@@ -63,7 +63,7 @@
 
     Result<void> AddCommand(std::vector<std::string>&& args, int line);
     void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);
-    std::size_t NumCommands() const;
+    size_t NumCommands() const;
     void ExecuteOneCommand(std::size_t command) const;
     void ExecuteAllCommands() const;
     bool CheckEvent(const EventTrigger& event_trigger) const;
@@ -71,11 +71,12 @@
     bool CheckEvent(const BuiltinAction& builtin_action) const;
     std::string BuildTriggersString() const;
     void DumpState() const;
+    size_t CheckAllCommands() const;
 
     bool oneshot() const { return oneshot_; }
     const std::string& filename() const { return filename_; }
     int line() const { return line_; }
-    static void set_function_map(const KeywordFunctionMap* function_map) {
+    static void set_function_map(const BuiltinFunctionMap* function_map) {
         function_map_ = function_map;
     }
 
@@ -91,10 +92,8 @@
     Subcontext* subcontext_;
     std::string filename_;
     int line_;
-    static const KeywordFunctionMap* function_map_;
+    static const BuiltinFunctionMap* function_map_;
 };
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 9de4085..ebca762 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -23,6 +23,14 @@
 
 ActionManager::ActionManager() : current_command_(0) {}
 
+size_t ActionManager::CheckAllCommands() {
+    size_t failures = 0;
+    for (const auto& action : actions_) {
+        failures += action->CheckAllCommands();
+    }
+    return failures;
+}
+
 ActionManager& ActionManager::GetInstance() {
     static ActionManager instance;
     return instance;
@@ -47,7 +55,7 @@
 void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
     auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
                                            std::map<std::string, std::string>{});
-    action->AddCommand(func, {name}, 0);
+    action->AddCommand(std::move(func), {name}, 0);
 
     event_queue_.emplace(action.get());
     actions_.emplace_back(std::move(action));
@@ -88,7 +96,8 @@
         current_command_ = 0;
         if (action->oneshot()) {
             auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
-            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
+                           actions_.end());
         }
     }
 }
diff --git a/init/action_manager.h b/init/action_manager.h
index 5f47a6d..a2b95ac 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_ACTION_MANAGER_H
-#define _INIT_ACTION_MANAGER_H
+#pragma once
 
 #include <string>
 #include <vector>
@@ -32,6 +31,7 @@
 
     // Exposed for testing
     ActionManager();
+    size_t CheckAllCommands();
 
     void AddAction(std::unique_ptr<Action> action);
     void QueueEventTrigger(const std::string& trigger);
@@ -55,5 +55,3 @@
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/builtins.cpp b/init/builtins.cpp
index cc84aa0..7c66de5 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -42,6 +42,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <ApexProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -54,7 +55,6 @@
 #include <cutils/android_reboot.h>
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
-#include <fscrypt/fscrypt_init_extensions.h>
 #include <libgsi/libgsi.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
@@ -63,6 +63,7 @@
 
 #include "action_manager.h"
 #include "bootchart.h"
+#include "fscrypt_init_extensions.h"
 #include "init.h"
 #include "mount_namespace.h"
 #include "parser.h"
@@ -88,6 +89,43 @@
 namespace android {
 namespace init {
 
+// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
+// device, such as '/sys/class/leds/jogball-backlight/brightness'.  As of this writing, there
+// are 81 such failures on cuttlefish.  Instead of spamming the log reporting them, we do not
+// report such failures unless we're running at the DEBUG log level.
+class ErrorIgnoreEnoent {
+  public:
+    ErrorIgnoreEnoent()
+        : ignore_error_(errno == ENOENT &&
+                        android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
+    explicit ErrorIgnoreEnoent(int errno_to_append)
+        : error_(errno_to_append),
+          ignore_error_(errno_to_append == ENOENT &&
+                        android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
+
+    template <typename T>
+    operator android::base::expected<T, ResultError>() {
+        if (ignore_error_) {
+            return {};
+        }
+        return error_;
+    }
+
+    template <typename T>
+    ErrorIgnoreEnoent& operator<<(T&& t) {
+        error_ << t;
+        return *this;
+    }
+
+  private:
+    Error error_;
+    bool ignore_error_;
+};
+
+inline ErrorIgnoreEnoent ErrnoErrorIgnoreEnoent() {
+    return ErrorIgnoreEnoent(errno);
+}
+
 std::vector<std::string> late_import_paths;
 
 static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
@@ -130,6 +168,13 @@
     if (args.context != kInitContext) {
         return Error() << "command 'class_start_post_data' only available in init context";
     }
+    static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+
+    if (!is_apex_updatable) {
+        // No need to start these on devices that don't support APEX, since they're not
+        // stopped either.
+        return {};
+    }
     for (const auto& service : ServiceList::GetInstance()) {
         if (service->classnames().count(args[1])) {
             if (auto result = service->StartIfPostData(); !result) {
@@ -155,6 +200,11 @@
     if (args.context != kInitContext) {
         return Error() << "command 'class_reset_post_data' only available in init context";
     }
+    static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+    if (!is_apex_updatable) {
+        // No need to stop these on devices that don't support APEX.
+        return {};
+    }
     ForEachServiceInClass(args[1], &Service::ResetIfPostData);
     return {};
 }
@@ -188,26 +238,26 @@
 static Result<void> do_exec(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        return Error() << "Could not create exec service";
+        return Error() << "Could not create exec service: " << service.error();
     }
-    if (auto result = service->ExecStart(); !result) {
+    if (auto result = (*service)->ExecStart(); !result) {
         return Error() << "Could not start exec service: " << result.error();
     }
 
-    ServiceList::GetInstance().AddService(std::move(service));
+    ServiceList::GetInstance().AddService(std::move(*service));
     return {};
 }
 
 static Result<void> do_exec_background(const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        return Error() << "Could not create exec background service";
+        return Error() << "Could not create exec background service: " << service.error();
     }
-    if (auto result = service->Start(); !result) {
+    if (auto result = (*service)->Start(); !result) {
         return Error() << "Could not start exec background service: " << result.error();
     }
 
-    ServiceList::GetInstance().AddService(std::move(service));
+    ServiceList::GetInstance().AddService(std::move(*service));
     return {};
 }
 
@@ -243,7 +293,7 @@
 
     strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
 
-    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM, 0)));
+    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
     if (s < 0) return ErrnoError() << "opening socket failed";
 
     if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
@@ -306,51 +356,64 @@
 // mkdir <path> [mode] [owner] [group]
 static Result<void> do_mkdir(const BuiltinArguments& args) {
     mode_t mode = 0755;
-    if (args.size() >= 3) {
-        mode = std::strtoul(args[2].c_str(), 0, 8);
-    }
+    Result<uid_t> uid = -1;
+    Result<gid_t> gid = -1;
 
-    if (!make_dir(args[1], mode)) {
-        /* chmod in case the directory already exists */
-        if (errno == EEXIST) {
-            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
-                return ErrnoError() << "fchmodat() failed";
-            }
-        } else {
-            return ErrnoError() << "mkdir() failed";
-        }
-    }
-
-    if (args.size() >= 4) {
-        auto uid = DecodeUid(args[3]);
-        if (!uid) {
-            return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
-        }
-        Result<gid_t> gid = -1;
-
-        if (args.size() == 5) {
+    switch (args.size()) {
+        case 5:
             gid = DecodeUid(args[4]);
             if (!gid) {
-                return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
+                return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
             }
-        }
-
-        if (lchown(args[1].c_str(), *uid, *gid) == -1) {
-            return ErrnoError() << "lchown failed";
-        }
-
-        /* chown may have cleared S_ISUID and S_ISGID, chmod again */
-        if (mode & (S_ISUID | S_ISGID)) {
-            if (fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
-                return ErrnoError() << "fchmodat failed";
+            FALLTHROUGH_INTENDED;
+        case 4:
+            uid = DecodeUid(args[3]);
+            if (!uid) {
+                return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
             }
+            FALLTHROUGH_INTENDED;
+        case 3:
+            mode = std::strtoul(args[2].c_str(), 0, 8);
+            FALLTHROUGH_INTENDED;
+        case 2:
+            break;
+        default:
+            return Error() << "Unexpected argument count: " << args.size();
+    }
+    std::string target = args[1];
+    struct stat mstat;
+    if (lstat(target.c_str(), &mstat) != 0) {
+        if (errno != ENOENT) {
+            return ErrnoError() << "lstat() failed on " << target;
+        }
+        if (!make_dir(target, mode)) {
+            return ErrnoErrorIgnoreEnoent() << "mkdir() failed on " << target;
+        }
+        if (lstat(target.c_str(), &mstat) != 0) {
+            return ErrnoError() << "lstat() failed on new " << target;
         }
     }
-
+    if (!S_ISDIR(mstat.st_mode)) {
+        return Error() << "Not a directory on " << target;
+    }
+    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != mode;
+    if ((*uid != static_cast<uid_t>(-1) && *uid != mstat.st_uid) ||
+        (*gid != static_cast<gid_t>(-1) && *gid != mstat.st_gid)) {
+        if (lchown(target.c_str(), *uid, *gid) == -1) {
+            return ErrnoError() << "lchown failed on " << target;
+        }
+        // chown may have cleared S_ISUID and S_ISGID, chmod again
+        needs_chmod = true;
+    }
+    if (needs_chmod) {
+        if (fchmodat(AT_FDCWD, target.c_str(), mode, AT_SYMLINK_NOFOLLOW) == -1) {
+            return ErrnoError() << "fchmodat() failed on " << target;
+        }
+    }
     if (fscrypt_is_native()) {
-        if (fscrypt_set_directory_policy(args[1].c_str())) {
+        if (fscrypt_set_directory_policy(target)) {
             return reboot_into_recovery(
-                {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
+                    {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + target});
         }
     }
     return {};
@@ -446,7 +509,7 @@
         if (wait)
             wait_for_file(source, kCommandRetryTimeout);
         if (mount(source, target, system, flags, options) < 0) {
-            return ErrnoError() << "mount() failed";
+            return ErrnoErrorIgnoreEnoent() << "mount() failed";
         }
 
     }
@@ -590,12 +653,7 @@
     if (!ReadFstabFromFile(fstab_file, &fstab)) {
         return Error() << "Could not read fstab";
     }
-
-    auto mount_fstab_return_code =
-            CallFunctionAndHandleProperties(fs_mgr_mount_all, &fstab, mount_mode);
-    if (!mount_fstab_return_code) {
-        return Error() << "Could not call fs_mgr_mount_all(): " << mount_fstab_return_code.error();
-    }
+    auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
     property_set(prop_name, std::to_string(t.duration().count()));
 
     if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
@@ -606,7 +664,7 @@
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
-        auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+        auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
         if (!queue_fs_result) {
             return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
         }
@@ -622,13 +680,8 @@
         return Error() << "Could not read fstab";
     }
 
-    auto result = CallFunctionAndHandleProperties(fs_mgr_umount_all, &fstab);
-    if (!result) {
-        return Error() << "Could not call fs_mgr_mount_all() " << result.error();
-    }
-
-    if (*result != 0) {
-        return Error() << "fs_mgr_mount_all() failed: " << *result;
+    if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
+        return Error() << "umount_fstab() failed " << result;
     }
     return {};
 }
@@ -639,13 +692,8 @@
         return Error() << "Could not read fstab '" << args[1] << "'";
     }
 
-    auto result = CallFunctionAndHandleProperties(fs_mgr_swapon_all, fstab);
-    if (!result) {
-        return Error() << "Could not call fs_mgr_swapon_all() " << result.error();
-    }
-
-    if (*result == 0) {
-        return Error() << "fs_mgr_swapon_all() failed.";
+    if (!fs_mgr_swapon_all(fstab)) {
+        return Error() << "fs_mgr_swapon_all() failed";
     }
 
     return {};
@@ -670,7 +718,7 @@
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
     if (auto result = svc->Start(); !result) {
-        return Error() << "Could not start service: " << result.error();
+        return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
     }
     return {};
 }
@@ -716,10 +764,7 @@
     if (MakeSymlink(args[1], args[2]) < 0) {
         // The symlink builtin is often used to create symlinks for older devices to be backwards
         // compatible with new paths, therefore we skip reporting this error.
-        if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
-            return {};
-        }
-        return ErrnoError() << "symlink() failed";
+        return ErrnoErrorIgnoreEnoent() << "symlink() failed";
     }
     return {};
 }
@@ -777,14 +822,15 @@
 
 static Result<void> do_write(const BuiltinArguments& args) {
     if (auto result = WriteFile(args[1], args[2]); !result) {
-        return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
+        return ErrorIgnoreEnoent()
+               << "Unable to write to file '" << args[1] << "': " << result.error();
     }
 
     return {};
 }
 
 static Result<void> readahead_file(const std::string& filename, bool fully) {
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_CLOEXEC)));
     if (fd == -1) {
         return ErrnoError() << "Error opening file";
     }
@@ -895,7 +941,7 @@
     }
 
     if (lchown(path.c_str(), *uid, *gid) == -1) {
-        return ErrnoError() << "lchown() failed";
+        return ErrnoErrorIgnoreEnoent() << "lchown() failed";
     }
 
     return {};
@@ -917,50 +963,27 @@
 static Result<void> do_chmod(const BuiltinArguments& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
-        return ErrnoError() << "fchmodat() failed";
+        return ErrnoErrorIgnoreEnoent() << "fchmodat() failed";
     }
     return {};
 }
 
 static Result<void> do_restorecon(const BuiltinArguments& args) {
+    auto restorecon_info = ParseRestorecon(args.args);
+    if (!restorecon_info) {
+        return restorecon_info.error();
+    }
+
+    const auto& [flag, paths] = *restorecon_info;
+
     int ret = 0;
-
-    struct flag_type {const char* name; int value;};
-    static const flag_type flags[] = {
-        {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
-        {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
-        {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
-        {0, 0}
-    };
-
-    int flag = 0;
-
-    bool in_flags = true;
-    for (size_t i = 1; i < args.size(); ++i) {
-        if (android::base::StartsWith(args[i], "--")) {
-            if (!in_flags) {
-                return Error() << "flags must precede paths";
-            }
-            bool found = false;
-            for (size_t j = 0; flags[j].name; ++j) {
-                if (args[i] == flags[j].name) {
-                    flag |= flags[j].value;
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) {
-                return Error() << "bad flag " << args[i];
-            }
-        } else {
-            in_flags = false;
-            if (selinux_android_restorecon(args[i].c_str(), flag) < 0) {
-                ret = errno;
-            }
+    for (const auto& path : paths) {
+        if (selinux_android_restorecon(path.c_str(), flag) < 0) {
+            ret = errno;
         }
     }
 
-    if (ret) return ErrnoError() << "selinux_android_restorecon() failed";
+    if (ret) return ErrnoErrorIgnoreEnoent() << "selinux_android_restorecon() failed";
     return {};
 }
 
@@ -1043,9 +1066,9 @@
                                             const BuiltinArguments& args) {
     auto service = Service::MakeTemporaryOneshotService(args.args);
     if (!service) {
-        return Error() << "Could not create exec service";
+        return Error() << "Could not create exec service: " << service.error();
     }
-    service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
+    (*service)->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
             // TODO (b/122850122): support this in gsi
             if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
@@ -1060,10 +1083,10 @@
             }
         }
     });
-    if (auto result = service->ExecStart(); !result) {
+    if (auto result = (*service)->ExecStart(); !result) {
         return Error() << "Could not start exec service: " << result.error();
     }
-    ServiceList::GetInstance().AddService(std::move(service));
+    ServiceList::GetInstance().AddService(std::move(*service));
     return {};
 }
 
@@ -1107,7 +1130,7 @@
         // /apex/<name> paths, so unless we filter them out, we will parse the
         // same file twice.
         std::vector<std::string> paths = android::base::Split(path, "/");
-        if (paths.size() >= 2 && paths[1].find('@') != std::string::npos) {
+        if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
             continue;
         }
         configs.push_back(path);
@@ -1139,10 +1162,10 @@
 }
 
 // Builtin-function-map start
-const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap& GetBuiltinFunctionMap() {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
-    static const Map builtin_functions = {
+    static const BuiltinFunctionMap builtin_functions = {
         {"bootchart",               {1,     1,    {false,  do_bootchart}}},
         {"chmod",                   {2,     2,    {true,   do_chmod}}},
         {"chown",                   {2,     3,    {true,   do_chown}}},
diff --git a/init/builtins.h b/init/builtins.h
index 7bbf6aa..f0ff1eb 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_BUILTINS_H
-#define _INIT_BUILTINS_H
+#pragma once
 
 #include <functional>
 #include <map>
@@ -31,18 +30,16 @@
 
 using BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;
 
-using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
-class BuiltinFunctionMap : public KeywordFunctionMap {
-  public:
-    BuiltinFunctionMap() {}
-
-  private:
-    const Map& map() const override;
+struct BuiltinFunctionMapValue {
+    bool run_in_subcontext;
+    BuiltinFunction function;
 };
 
+using BuiltinFunctionMap = KeywordMap<BuiltinFunctionMapValue>;
+
+const BuiltinFunctionMap& GetBuiltinFunctionMap();
+
 extern std::vector<std::string> late_import_paths;
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
new file mode 100644
index 0000000..771f1d7
--- /dev/null
+++ b/init/check_builtins.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Note that these check functions cannot check expanded arguments from properties, since they will
+// not know what those properties would be at runtime.  They will be passed an empty string in the
+// situation that the input line had a property expansion without a default value, since an empty
+// string is otherwise an impossible value.  They should therefore disregard checking empty
+// arguments.
+
+#include "check_builtins.h"
+
+#include <sys/time.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "builtin_arguments.h"
+#include "interface_utils.h"
+#include "rlimit_parser.h"
+#include "service.h"
+#include "util.h"
+
+using android::base::ParseInt;
+using android::base::StartsWith;
+
+#define ReturnIfAnyArgsEmpty()     \
+    for (const auto& arg : args) { \
+        if (arg.empty()) {         \
+            return {};             \
+        }                          \
+    }
+
+namespace android {
+namespace init {
+
+Result<void> check_chown(const BuiltinArguments& args) {
+    if (!args[1].empty()) {
+        auto uid = DecodeUid(args[1]);
+        if (!uid) {
+            return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
+        }
+    }
+
+    // GID is optional and pushes the index of path out by one if specified.
+    if (args.size() == 4 && !args[2].empty()) {
+        auto gid = DecodeUid(args[2]);
+        if (!gid) {
+            return Error() << "Unable to decode GID for '" << args[2] << "': " << gid.error();
+        }
+    }
+
+    return {};
+}
+
+Result<void> check_exec(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    auto result = Service::MakeTemporaryOneshotService(args.args);
+    if (!result) {
+        return result.error();
+    }
+
+    return {};
+}
+
+Result<void> check_exec_background(const BuiltinArguments& args) {
+    return check_exec(std::move(args));
+}
+
+Result<void> check_interface_restart(const BuiltinArguments& args) {
+    if (auto result = IsKnownInterface(args[1]); !result) {
+        return result.error();
+    }
+    return {};
+}
+
+Result<void> check_interface_start(const BuiltinArguments& args) {
+    return check_interface_restart(std::move(args));
+}
+
+Result<void> check_interface_stop(const BuiltinArguments& args) {
+    return check_interface_restart(std::move(args));
+}
+
+Result<void> check_load_system_props(const BuiltinArguments& args) {
+    return Error() << "'load_system_props' is deprecated";
+}
+
+Result<void> check_loglevel(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    int log_level = -1;
+    ParseInt(args[1], &log_level);
+    if (log_level < 0 || log_level > 7) {
+        return Error() << "loglevel must be in the range of 0-7";
+    }
+    return {};
+}
+
+Result<void> check_mkdir(const BuiltinArguments& args) {
+    if (args.size() >= 4) {
+        if (!args[3].empty()) {
+            auto uid = DecodeUid(args[3]);
+            if (!uid) {
+                return Error() << "Unable to decode UID for '" << args[3] << "': " << uid.error();
+            }
+        }
+
+        if (args.size() == 5 && !args[4].empty()) {
+            auto gid = DecodeUid(args[4]);
+            if (!gid) {
+                return Error() << "Unable to decode GID for '" << args[4] << "': " << gid.error();
+            }
+        }
+    }
+
+    return {};
+}
+
+Result<void> check_restorecon(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    auto restorecon_info = ParseRestorecon(args.args);
+    if (!restorecon_info) {
+        return restorecon_info.error();
+    }
+
+    return {};
+}
+
+Result<void> check_restorecon_recursive(const BuiltinArguments& args) {
+    return check_restorecon(std::move(args));
+}
+
+Result<void> check_setprop(const BuiltinArguments& args) {
+    const std::string& name = args[1];
+    if (name.empty()) {
+        return {};
+    }
+    const std::string& value = args[2];
+
+    if (!IsLegalPropertyName(name)) {
+        return Error() << "'" << name << "' is not a legal property name";
+    }
+
+    if (!value.empty()) {
+        if (auto result = IsLegalPropertyValue(name, value); !result) {
+            return result.error();
+        }
+    }
+
+    if (StartsWith(name, "ctl.")) {
+        return Error()
+               << "Do not set ctl. properties from init; call the Service functions directly";
+    }
+
+    static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
+    if (name == kRestoreconProperty) {
+        return Error() << "Do not set '" << kRestoreconProperty
+                       << "' from init; use the restorecon builtin directly";
+    }
+
+    return {};
+}
+
+Result<void> check_setrlimit(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    auto rlimit = ParseRlimit(args.args);
+    if (!rlimit) return rlimit.error();
+    return {};
+}
+
+Result<void> check_sysclktz(const BuiltinArguments& args) {
+    ReturnIfAnyArgsEmpty();
+
+    struct timezone tz = {};
+    if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
+        return Error() << "Unable to parse mins_west_of_gmt";
+    }
+    return {};
+}
+
+Result<void> check_wait(const BuiltinArguments& args) {
+    if (args.size() == 3 && !args[2].empty()) {
+        int timeout_int;
+        if (!android::base::ParseInt(args[2], &timeout_int)) {
+            return Error() << "failed to parse timeout";
+        }
+    }
+    return {};
+}
+
+Result<void> check_wait_for_prop(const BuiltinArguments& args) {
+    return check_setprop(std::move(args));
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/check_builtins.h b/init/check_builtins.h
new file mode 100644
index 0000000..4ff0d0c
--- /dev/null
+++ b/init/check_builtins.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<void> check_chown(const BuiltinArguments& args);
+Result<void> check_exec(const BuiltinArguments& args);
+Result<void> check_exec_background(const BuiltinArguments& args);
+Result<void> check_interface_restart(const BuiltinArguments& args);
+Result<void> check_interface_start(const BuiltinArguments& args);
+Result<void> check_interface_stop(const BuiltinArguments& args);
+Result<void> check_load_system_props(const BuiltinArguments& args);
+Result<void> check_loglevel(const BuiltinArguments& args);
+Result<void> check_mkdir(const BuiltinArguments& args);
+Result<void> check_restorecon(const BuiltinArguments& args);
+Result<void> check_restorecon_recursive(const BuiltinArguments& args);
+Result<void> check_setprop(const BuiltinArguments& args);
+Result<void> check_setrlimit(const BuiltinArguments& args);
+Result<void> check_sysclktz(const BuiltinArguments& args);
+Result<void> check_wait(const BuiltinArguments& args);
+Result<void> check_wait_for_prop(const BuiltinArguments& args);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
deleted file mode 100644
index 6265687..0000000
--- a/init/descriptors.cpp
+++ /dev/null
@@ -1,132 +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 "descriptors.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <cutils/android_get_control_file.h>
-#include <cutils/sockets.h>
-
-#include "util.h"
-
-namespace android {
-namespace init {
-
-DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
-                               gid_t gid, int perm, const std::string& context)
-        : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
-}
-
-DescriptorInfo::~DescriptorInfo() {
-}
-
-std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
-  return os << "  descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
-}
-
-bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
-  return name_ == other.name_ && type_ == other.type_ && key() == other.key();
-}
-
-void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
-  // Create
-  const std::string& contextStr = context_.empty() ? globalContext : context_;
-  int fd = Create(contextStr);
-  if (fd < 0) return;
-
-  // Publish
-  std::string publishedName = key() + name_;
-  std::for_each(publishedName.begin(), publishedName.end(),
-                [] (char& c) { c = isalnum(c) ? c : '_'; });
-
-  std::string val = std::to_string(fd);
-  setenv(publishedName.c_str(), val.c_str(), 1);
-
-  // make sure we don't close on exec
-  fcntl(fd, F_SETFD, 0);
-}
-
-void DescriptorInfo::Clean() const {
-}
-
-SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
-                       gid_t gid, int perm, const std::string& context)
-        : DescriptorInfo(name, type, uid, gid, perm, context) {
-}
-
-void SocketInfo::Clean() const {
-    std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
-    unlink(path.c_str());
-}
-
-int SocketInfo::Create(const std::string& context) const {
-    auto types = android::base::Split(type(), "+");
-    int flags =
-        ((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
-    bool passcred = types.size() > 1 && types[1] == "passcred";
-    return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
-}
-
-const std::string SocketInfo::key() const {
-  return ANDROID_SOCKET_ENV_PREFIX;
-}
-
-FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
-                   gid_t gid, int perm, const std::string& context)
-        // defaults OK for uid,..., they are ignored for this class.
-        : DescriptorInfo(name, type, uid, gid, perm, context) {
-}
-
-int FileInfo::Create(const std::string&) const {
-  int flags = (type() == "r") ? O_RDONLY :
-              (type() == "w") ? O_WRONLY :
-                                O_RDWR;
-
-  // Make sure we do not block on open (eg: devices can chose to block on
-  // carrier detect).  Our intention is never to delay launch of a service
-  // for such a condition.  The service can perform its own blocking on
-  // carrier detect.
-  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name().c_str(),
-                                                      flags | O_NONBLOCK)));
-
-  if (fd < 0) {
-    PLOG(ERROR) << "Failed to open file '" << name().c_str() << "'";
-    return -1;
-  }
-
-  // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
-  fcntl(fd, F_SETFL, flags);
-
-  LOG(INFO) << "Opened file '" << name().c_str() << "'"
-            << ", flags " << std::oct << flags << std::dec;
-
-  return fd.release();
-}
-
-const std::string FileInfo::key() const {
-  return ANDROID_FILE_ENV_PREFIX;
-}
-
-}  // namespace init
-}  // namespace android
diff --git a/init/descriptors.h b/init/descriptors.h
deleted file mode 100644
index 3bdddfe..0000000
--- a/init/descriptors.h
+++ /dev/null
@@ -1,84 +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 _INIT_DESCRIPTORS_H
-#define _INIT_DESCRIPTORS_H
-
-#include <sys/types.h>
-
-#include <string>
-
-namespace android {
-namespace init {
-
-class DescriptorInfo {
- public:
-  DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
-                 gid_t gid, int perm, const std::string& context);
-  virtual ~DescriptorInfo();
-
-  friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
-  bool operator==(const DescriptorInfo& other) const;
-
-  void CreateAndPublish(const std::string& globalContext) const;
-  virtual void Clean() const;
-
- protected:
-  const std::string& name() const { return name_; }
-  const std::string& type() const { return type_; }
-  uid_t uid() const { return uid_; }
-  gid_t gid() const { return gid_; }
-  int perm() const { return perm_; }
-  const std::string& context() const { return context_; }
-
- private:
-  std::string name_;
-  std::string type_;
-  uid_t uid_;
-  gid_t gid_;
-  int perm_;
-  std::string context_;
-
-  virtual int Create(const std::string& globalContext) const = 0;
-  virtual const std::string key() const = 0;
-};
-
-std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
-
-class SocketInfo : public DescriptorInfo {
- public:
-  SocketInfo(const std::string& name, const std::string& type, uid_t uid,
-             gid_t gid, int perm, const std::string& context);
-  void Clean() const override;
- private:
-  virtual int Create(const std::string& context) const override;
-  virtual const std::string key() const override;
-};
-
-class FileInfo : public DescriptorInfo {
- public:
-  FileInfo(const std::string& name, const std::string& type, uid_t uid,
-           gid_t gid, int perm, const std::string& context);
- private:
-  virtual int Create(const std::string& context) const override;
-  virtual const std::string key() const override;
-};
-
-}  // namespace init
-}  // namespace android
-
-#endif
diff --git a/init/devices.cpp b/init/devices.cpp
index e8e6cd7..f6e453a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -22,7 +22,6 @@
 #include <unistd.h>
 
 #include <chrono>
-#include <map>
 #include <memory>
 #include <string>
 #include <thread>
@@ -111,24 +110,16 @@
 // the supplied buffer with the dm module's instantiated name.
 // If it doesn't start with a virtual block device, or there is some
 // error, return false.
-static bool FindDmDevicePartition(const std::string& path, std::string* result) {
-    result->clear();
+static bool FindDmDevice(const std::string& path, std::string* name, std::string* uuid) {
     if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
-    if (getpid() == 1) return false;  // first_stage_init has no sepolicy needs
 
-    static std::map<std::string, std::string> cache;
-    // wait_for_file will not work, the content is also delayed ...
-    for (android::base::Timer t; t.duration() < 200ms; std::this_thread::sleep_for(10ms)) {
-        if (ReadFileToString("/sys" + path + "/dm/name", result) && !result->empty()) {
-            // Got it, set cache with result, when node arrives
-            cache[path] = *result = Trim(*result);
-            return true;
-        }
+    if (!ReadFileToString("/sys" + path + "/dm/name", name)) {
+        return false;
     }
-    auto it = cache.find(path);
-    if ((it == cache.end()) || (it->second.empty())) return false;
-    // Return cached results, when node goes away
-    *result = it->second;
+    ReadFileToString("/sys" + path + "/dm/uuid", uuid);
+
+    *name = android::base::Trim(*name);
+    *uuid = android::base::Trim(*uuid);
     return true;
 }
 
@@ -325,6 +316,7 @@
     std::string device;
     std::string type;
     std::string partition;
+    std::string uuid;
 
     if (FindPlatformDevice(uevent.path, &device)) {
         // Skip /devices/platform or /devices/ if present
@@ -342,8 +334,12 @@
         type = "pci";
     } else if (FindVbdDevicePrefix(uevent.path, &device)) {
         type = "vbd";
-    } else if (FindDmDevicePartition(uevent.path, &partition)) {
-        return {"/dev/block/mapper/" + partition};
+    } else if (FindDmDevice(uevent.path, &partition, &uuid)) {
+        std::vector<std::string> symlinks = {"/dev/block/mapper/" + partition};
+        if (!uuid.empty()) {
+            symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
+        }
+        return symlinks;
     } else {
         return {};
     }
@@ -379,10 +375,41 @@
     return links;
 }
 
+static void RemoveDeviceMapperLinks(const std::string& devpath) {
+    std::vector<std::string> dirs = {
+            "/dev/block/mapper",
+            "/dev/block/mapper/by-uuid",
+    };
+    for (const auto& dir : dirs) {
+        if (access(dir.c_str(), F_OK) != 0) continue;
+
+        std::unique_ptr<DIR, decltype(&closedir)> dh(opendir(dir.c_str()), closedir);
+        if (!dh) {
+            PLOG(ERROR) << "Failed to open directory " << dir;
+            continue;
+        }
+
+        struct dirent* dp;
+        std::string link_path;
+        while ((dp = readdir(dh.get())) != nullptr) {
+            if (dp->d_type != DT_LNK) continue;
+
+            auto path = dir + "/" + dp->d_name;
+            if (Readlink(path, &link_path) && link_path == devpath) {
+                unlink(path.c_str());
+            }
+        }
+    }
+}
+
 void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
                                  int major, int minor, const std::vector<std::string>& links) const {
     if (action == "add") {
         MakeDevice(devpath, block, major, minor, links);
+    }
+
+    // We don't have full device-mapper information until a change event is fired.
+    if (action == "add" || (action == "change" && StartsWith(devpath, "/dev/block/dm-"))) {
         for (const auto& link : links) {
             if (!mkdir_recursive(Dirname(link), 0755)) {
                 PLOG(ERROR) << "Failed to create directory " << Dirname(link);
@@ -401,6 +428,9 @@
     }
 
     if (action == "remove") {
+        if (StartsWith(devpath, "/dev/block/dm-")) {
+            RemoveDeviceMapperLinks(devpath);
+        }
         for (const auto& link : links) {
             std::string link_path;
             if (Readlink(link, &link_path) && link_path == devpath) {
diff --git a/init/devices.h b/init/devices.h
index 9d39eaa..442c53f 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -84,9 +84,9 @@
     };
 
     Subsystem() {}
-    Subsystem(const std::string& name) : name_(name) {}
-    Subsystem(const std::string& name, DevnameSource source, const std::string& dir_name)
-        : name_(name), devname_source_(source), dir_name_(dir_name) {}
+    Subsystem(std::string name) : name_(std::move(name)) {}
+    Subsystem(std::string name, DevnameSource source, std::string dir_name)
+        : name_(std::move(name)), devname_source_(source), dir_name_(std::move(dir_name)) {}
 
     // Returns the full path for a uevent of a device that is a member of this subsystem,
     // according to the rules parsed from ueventd.rc
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 3e7c1a8..c408bc1 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -30,7 +30,7 @@
 class DeviceHandlerTester {
   public:
     void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
-                         const std::vector<std::string> expected_links) {
+                         const std::vector<std::string>& expected_links) {
         TemporaryDir fake_sys_root;
         device_handler_.sysfs_mount_point_ = fake_sys_root.path;
 
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 17387e2..fd2d766 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -24,10 +24,12 @@
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #include <filesystem>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -76,7 +78,7 @@
 
             if (S_ISDIR(info.st_mode)) {
                 is_dir = true;
-                auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+                auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
                 if (fd >= 0) {
                     auto subdir =
                             std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
@@ -92,9 +94,50 @@
     }
 }
 
-bool ForceNormalBoot() {
-    std::string cmdline;
-    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+void StartConsole() {
+    if (mknod("/dev/console", S_IFCHR | 0600, makedev(5, 1))) {
+        PLOG(ERROR) << "unable to create /dev/console";
+        return;
+    }
+    pid_t pid = fork();
+    if (pid != 0) {
+        int status;
+        waitpid(pid, &status, 0);
+        LOG(ERROR) << "console shell exited with status " << status;
+        return;
+    }
+    int fd = -1;
+    int tries = 10;
+    // The device driver for console may not be ready yet so retry for a while in case of failure.
+    while (tries--) {
+        fd = open("/dev/console", O_RDWR);
+        if (fd != -1) {
+            break;
+        }
+        std::this_thread::sleep_for(100ms);
+    }
+    if (fd == -1) {
+        LOG(ERROR) << "Could not open /dev/console, errno = " << errno;
+        _exit(127);
+    }
+    ioctl(fd, TIOCSCTTY, 0);
+    dup2(fd, STDIN_FILENO);
+    dup2(fd, STDOUT_FILENO);
+    dup2(fd, STDERR_FILENO);
+    close(fd);
+
+    const char* path = "/system/bin/sh";
+    const char* args[] = {path, nullptr};
+    int rv = execv(path, const_cast<char**>(args));
+    LOG(ERROR) << "unable to execv, returned " << rv << " errno " << errno;
+    _exit(127);
+}
+
+bool FirstStageConsole(const std::string& cmdline) {
+    return cmdline.find("androidboot.first_stage_console=1") != std::string::npos;
+}
+
+bool ForceNormalBoot(const std::string& cmdline) {
     return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
@@ -109,7 +152,7 @@
 
     std::vector<std::pair<std::string, int>> errors;
 #define CHECKCALL(x) \
-    if (x != 0) errors.emplace_back(#x " failed", errno);
+    if ((x) != 0) errors.emplace_back(#x " failed", errno);
 
     // Clear the umask.
     umask(0);
@@ -127,6 +170,8 @@
 #undef MAKE_STR
     // Don't expose the raw commandline to unprivileged processes.
     CHECKCALL(chmod("/proc/cmdline", 0440));
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
     gid_t groups[] = {AID_READPROC};
     CHECKCALL(setgroups(arraysize(groups), groups));
     CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
@@ -198,7 +243,11 @@
         LOG(FATAL) << "Failed to load kernel modules";
     }
 
-    if (ForceNormalBoot()) {
+    if (ALLOW_FIRST_STAGE_CONSOLE && FirstStageConsole(cmdline)) {
+        StartConsole();
+    }
+
+    if (ForceNormalBoot(cmdline)) {
         mkdir("/first_stage_ramdisk", 0755);
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
         // target directory to itself here.
@@ -242,6 +291,10 @@
 
     const char* path = "/system/bin/init";
     const char* args[] = {path, "selinux_setup", nullptr};
+    auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
+    dup2(fd, STDOUT_FILENO);
+    dup2(fd, STDERR_FILENO);
+    close(fd);
     execv(path, const_cast<char**>(args));
 
     // execv() only returns if an error happened, in which case we
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1a5ed28..6b4216f 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -36,6 +36,7 @@
 #include <fs_mgr_overlayfs.h>
 #include <libgsi/libgsi.h>
 #include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
 
 #include "devices.h"
 #include "switch_root.h"
@@ -55,6 +56,7 @@
 using android::fs_mgr::ReadDefaultFstab;
 using android::fs_mgr::ReadFstabFromDt;
 using android::fs_mgr::SkipMountingPartitions;
+using android::snapshot::SnapshotManager;
 
 using namespace std::literals;
 
@@ -244,8 +246,6 @@
 
     if (!InitDevices()) return false;
 
-    if (!CreateLogicalPartitions()) return false;
-
     if (!MountPartitions()) return false;
 
     return true;
@@ -366,6 +366,16 @@
         return false;
     }
 
+    if (SnapshotManager::IsSnapshotManagerNeeded()) {
+        auto sm = SnapshotManager::NewForFirstStageMount();
+        if (!sm) {
+            return false;
+        }
+        if (sm->NeedSnapshotsInFirstStageMount()) {
+            return sm->CreateLogicalAndSnapshotPartitions(lp_metadata_partition_);
+        }
+    }
+
     auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
     if (!metadata) {
         LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
@@ -493,14 +503,7 @@
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
 bool FirstStageMount::TrySwitchSystemAsRoot() {
-    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
-        return entry.mount_point == "/metadata";
-    });
-    if (metadata_partition != fstab_.end()) {
-        if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
-            UseGsiIfPresent();
-        }
-    }
+    UseGsiIfPresent();
 
     auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
         return entry.mount_point == "/system";
@@ -523,6 +526,17 @@
 }
 
 bool FirstStageMount::MountPartitions() {
+    // Mount /metadata before creating logical partitions, since we need to
+    // know whether a snapshot merge is in progress.
+    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/metadata";
+    });
+    if (metadata_partition != fstab_.end()) {
+        MountPartition(metadata_partition, true /* erase_same_mounts */);
+    }
+
+    if (!CreateLogicalPartitions()) return false;
+
     if (!TrySwitchSystemAsRoot()) return false;
 
     if (!SkipMountingPartitions(&fstab_)) return false;
@@ -602,19 +616,11 @@
         return;
     }
 
-    // Find the name of the super partition for the GSI. It will either be
-    // "userdata", or a block device such as an sdcard. There are no by-name
-    // partitions other than userdata that we support installing GSIs to.
+    // Find the super name. PartitionOpener will ensure this translates to the
+    // correct block device path.
     auto super = GetMetadataSuperBlockDevice(*metadata.get());
-    std::string super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
-    std::string super_path;
-    if (super_name == "userdata") {
-        super_path = "/dev/block/by-name/" + super_name;
-    } else {
-        super_path = "/dev/block/" + super_name;
-    }
-
-    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path)) {
+    auto super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
+    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_name)) {
         LOG(ERROR) << "GSI partition layout could not be instantiated";
         return;
     }
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
new file mode 100644
index 0000000..0f5a864
--- /dev/null
+++ b/init/fscrypt_init_extensions.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fscrypt_init_extensions.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fts.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <fscrypt/fscrypt.h>
+#include <keyutils.h>
+#include <logwrap/logwrap.h>
+
+#define TAG "fscrypt"
+
+static int set_system_de_policy_on(const std::string& dir);
+
+int fscrypt_install_keyring() {
+    key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, KEY_SPEC_SESSION_KEYRING);
+
+    if (device_keyring == -1) {
+        PLOG(ERROR) << "Failed to create keyring";
+        return -1;
+    }
+
+    LOG(INFO) << "Keyring created with id " << device_keyring << " in process " << getpid();
+
+    return 0;
+}
+
+// TODO(b/139378601): use a single central implementation of this.
+static void delete_dir_contents(const std::string& dir) {
+    char* const paths[2] = {const_cast<char*>(dir.c_str()), nullptr};
+    FTS* fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+    FTSENT* cur;
+    while ((cur = fts_read(fts)) != nullptr) {
+        if (cur->fts_info == FTS_ERR) {
+            PLOG(ERROR) << "fts_read";
+            break;
+        }
+        if (dir == cur->fts_path) {
+            continue;
+        }
+        switch (cur->fts_info) {
+            case FTS_D:
+                break;  // Ignore these
+            case FTS_DP:
+                if (rmdir(cur->fts_path) == -1) {
+                    PLOG(ERROR) << "rmdir " << cur->fts_path;
+                }
+                break;
+            default:
+                PLOG(ERROR) << "FTS unexpected type " << cur->fts_info << " at " << cur->fts_path;
+                if (rmdir(cur->fts_path) != -1) break;
+                // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
+                FALLTHROUGH_INTENDED;
+            case FTS_F:
+            case FTS_SL:
+            case FTS_SLNONE:
+                if (unlink(cur->fts_path) == -1) {
+                    PLOG(ERROR) << "unlink " << cur->fts_path;
+                }
+                break;
+        }
+    }
+
+    if (fts_close(fts) != 0) {
+        PLOG(ERROR) << "fts_close";
+    }
+}
+
+int fscrypt_set_directory_policy(const std::string& dir) {
+    const std::string prefix = "/data/";
+
+    if (!android::base::StartsWith(dir, prefix)) {
+        return 0;
+    }
+
+    // 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);
+        return 0;
+    }
+
+    // Only set policy on first level /data directories
+    // To make this less restrictive, consider using a policy file.
+    // However this is overkill for as long as the policy is simply
+    // to apply a global policy to all /data folders created via makedir
+    if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
+        return 0;
+    }
+
+    // Special case various directories that must not be encrypted,
+    // often because their subdirectories must be encrypted.
+    // This isn't a nice way to do this, see b/26641735
+    std::vector<std::string> directories_to_exclude = {
+        "lost+found",
+        "system_ce", "system_de",
+        "misc_ce", "misc_de",
+        "vendor_ce", "vendor_de",
+        "media",
+        "data", "user", "user_de",
+        "apex", "preloads", "app-staging",
+        "gsi",
+    };
+    for (const auto& d : directories_to_exclude) {
+        if ((prefix + d) == dir) {
+            LOG(INFO) << "Not setting policy on " << dir;
+            return 0;
+        }
+    }
+    int err = set_system_de_policy_on(dir);
+    if (err == 0) {
+        return 0;
+    }
+    // Empty these directories if policy setting fails.
+    std::vector<std::string> wipe_on_failure = {
+            "rollback", "rollback-observer",  // b/139193659
+    };
+    for (const auto& d : wipe_on_failure) {
+        if ((prefix + d) == dir) {
+            LOG(ERROR) << "Setting policy failed, deleting: " << dir;
+            delete_dir_contents(dir);
+            err = set_system_de_policy_on(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)) {
+        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";
+    }
+
+    std::vector<std::string> modes = android::base::Split(modestring, ":");
+
+    if (modes.size() < 1 || modes.size() > 2) {
+        LOG(ERROR) << "Invalid encryption mode string: " << modestring;
+        return -1;
+    }
+
+    LOG(INFO) << "Setting policy on " << dir;
+    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");
+    if (result) {
+        LOG(ERROR) << android::base::StringPrintf("Setting %02x%02x%02x%02x policy on %s failed!",
+                                                  policy[0], policy[1], policy[2], policy[3],
+                                                  dir.c_str());
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/demangle/include/demangle.h b/init/fscrypt_init_extensions.h
similarity index 61%
rename from demangle/include/demangle.h
rename to init/fscrypt_init_extensions.h
index 01f1b80..2163ef6 100644
--- a/demangle/include/demangle.h
+++ b/init/fscrypt_init_extensions.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef __LIB_DEMANGLE_H_
-#define __LIB_DEMANGLE_H_
+#pragma once
 
 #include <string>
 
-// If the name cannot be demangled, the original name will be returned as
-// a std::string. If the name can be demangled, then the demangled name
-// will be returned as a std::string.
-std::string demangle(const char* name);
-
-#endif  // __LIB_DEMANGLE_H_
+int fscrypt_install_keyring();
+int fscrypt_set_directory_policy(const std::string& dir);
diff --git a/init/host_builtin_map.py b/init/host_builtin_map.py
new file mode 100755
index 0000000..6afcb17
--- /dev/null
+++ b/init/host_builtin_map.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+"""Generates the builtins map to be used by host_init_verifier.
+
+It copies the builtin function map from builtins.cpp, then replaces do_xxx() functions with the
+equivalent check_xxx() if found in check_builtins.cpp.
+
+"""
+
+import re
+import argparse
+
+parser = argparse.ArgumentParser('host_builtin_map.py')
+parser.add_argument('--builtins', required=True, help='Path to builtins.cpp')
+parser.add_argument('--check_builtins', required=True, help='Path to check_builtins.cpp')
+args = parser.parse_args()
+
+CHECK_REGEX = re.compile(r'.+check_(\S+)\(.+')
+check_functions = []
+with open(args.check_builtins) as check_file:
+  for line in check_file:
+    match = CHECK_REGEX.match(line)
+    if match:
+      check_functions.append(match.group(1))
+
+function_map = []
+with open(args.builtins) as builtins_file:
+  in_function_map = False
+  for line in builtins_file:
+    if '// Builtin-function-map start' in line:
+      in_function_map = True
+    elif '// Builtin-function-map end' in line:
+      in_function_map = False
+    elif in_function_map:
+      function_map.append(line)
+
+DO_REGEX = re.compile(r'.+do_([^\}]+).+')
+FUNCTION_REGEX = re.compile(r'(do_[^\}]+)')
+for line in function_map:
+  match = DO_REGEX.match(line)
+  if match:
+    if match.group(1) in check_functions:
+      print line.replace('do_', 'check_'),
+    else:
+      print FUNCTION_REGEX.sub('check_stub', line),
+  else:
+    print line,
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 7c0544a..f9a08a5 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -26,6 +26,7 @@
 
 // android/api-level.h
 #define __ANDROID_API_P__ 28
+#define __ANDROID_API_R__ 30
 
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 9323aa0..b2402b3 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <fstream>
 #include <iostream>
 #include <iterator>
 #include <string>
@@ -33,8 +34,10 @@
 #include "action.h"
 #include "action_manager.h"
 #include "action_parser.h"
+#include "check_builtins.h"
 #include "host_import_parser.h"
 #include "host_init_stubs.h"
+#include "interface_utils.h"
 #include "parser.h"
 #include "result.h"
 #include "service.h"
@@ -132,18 +135,19 @@
 namespace android {
 namespace init {
 
-static Result<void> do_stub(const BuiltinArguments& args) {
+static Result<void> check_stub(const BuiltinArguments& args) {
     return {};
 }
 
 #include "generated_stub_builtin_function_map.h"
 
 void PrintUsage() {
-    std::cout << "usage: host_init_verifier [-p FILE] <init rc file>\n"
+    std::cout << "usage: host_init_verifier [-p FILE] -i 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;
 }
 
@@ -151,13 +155,15 @@
     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:", long_options, nullptr);
+        int arg = getopt_long(argc, argv, "p:i:", long_options, nullptr);
 
         if (arg == -1) {
             break;
@@ -170,6 +176,9 @@
             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;
@@ -179,17 +188,26 @@
     argc -= optind;
     argv += optind;
 
-    if (argc != 1) {
+    if (argc != 1 || interface_inheritance_hierarchy_file.empty()) {
         PrintUsage();
         return EXIT_FAILURE;
     }
 
-    const BuiltinFunctionMap function_map;
+    auto interface_inheritance_hierarchy_map =
+            ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file);
+    if (!interface_inheritance_hierarchy_map) {
+        LOG(ERROR) << interface_inheritance_hierarchy_map.error();
+        return EXIT_FAILURE;
+    }
+    SetKnownInterfaces(*interface_inheritance_hierarchy_map);
+
+    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sl = ServiceList::GetInstance();
     Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
+                                               &sl, nullptr, *interface_inheritance_hierarchy_map));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<HostImportParser>());
 
@@ -197,9 +215,10 @@
         LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
         return EXIT_FAILURE;
     }
-    if (parser.parse_error_count() > 0) {
-        LOG(ERROR) << "Failed to parse init script '" << *argv << "' with "
-                   << parser.parse_error_count() << " errors";
+    size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
+    if (failures > 0) {
+        LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures
+                   << " errors";
         return EXIT_FAILURE;
     }
     return EXIT_SUCCESS;
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index c72b7d6..1a43508 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -29,15 +29,14 @@
         return Error() << "single argument needed for import\n";
     }
 
-    std::string conf_file;
-    bool ret = expand_props(args[1], &conf_file);
-    if (!ret) {
-        return Error() << "error while expanding import";
+    auto conf_file = ExpandProps(args[1]);
+    if (!conf_file) {
+        return Error() << "Could not expand import: " << conf_file.error();
     }
 
-    LOG(INFO) << "Added '" << conf_file << "' to import list";
+    LOG(INFO) << "Added '" << *conf_file << "' to import list";
     if (filename_.empty()) filename_ = filename;
-    imports_.emplace_back(std::move(conf_file), line);
+    imports_.emplace_back(std::move(*conf_file), line);
     return {};
 }
 
diff --git a/init/init.cpp b/init/init.cpp
index b6911e5..18fb0c3 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -50,10 +50,6 @@
 #include <processgroup/setup.h>
 #include <selinux/android.h>
 
-#ifndef RECOVERY
-#include <binder/ProcessState.h>
-#endif
-
 #include "action_parser.h"
 #include "boringssl_self_test.h"
 #include "builtins.h"
@@ -113,7 +109,8 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -124,7 +121,8 @@
 Parser CreateServiceOnlyParser(ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
     return parser;
 }
 
@@ -137,12 +135,12 @@
         if (!parser.ParseConfig("/system/etc/init")) {
             late_import_paths.emplace_back("/system/etc/init");
         }
+        // late_import is available only in Q and earlier release. As we don't
+        // have system_ext in those versions, skip late_import for system_ext.
+        parser.ParseConfig("/system_ext/etc/init");
         if (!parser.ParseConfig("/product/etc/init")) {
             late_import_paths.emplace_back("/product/etc/init");
         }
-        if (!parser.ParseConfig("/product_services/etc/init")) {
-            late_import_paths.emplace_back("/product_services/etc/init");
-        }
         if (!parser.ParseConfig("/odm/etc/init")) {
             late_import_paths.emplace_back("/odm/etc/init");
         }
@@ -451,24 +449,6 @@
     return {};
 }
 
-static Result<void> InitBinder(const BuiltinArguments& args) {
-    // init's use of binder is very limited. init cannot:
-    //   - have any binder threads
-    //   - receive incoming binder calls
-    //   - pass local binder services to remote processes
-    //   - use death recipients
-    // The main supported usecases are:
-    //   - notifying other daemons (oneway calls only)
-    //   - retrieving data that is necessary to boot
-    // Also, binder can't be used by recovery.
-#ifndef RECOVERY
-    android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
-    android::ProcessState::self()->setCallRestriction(
-            ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY);
-#endif
-    return {};
-}
-
 // Set the UDC controller for the ConfigFS USB Gadgets.
 // Read the UDC controller in use from "/sys/class/udc".
 // In case of multiple UDC controllers select the first one.
@@ -708,7 +688,7 @@
     MountHandler mount_handler(&epoll);
     set_usb_controller();
 
-    const BuiltinFunctionMap function_map;
+    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
     Action::set_function_map(&function_map);
 
     if (!SetupMountNamespaces()) {
@@ -764,9 +744,6 @@
     // wasn't ready immediately after wait_for_coldboot_done
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
 
-    // Initialize binder before bringing up other system services
-    am.QueueBuiltinAction(InitBinder, "InitBinder");
-
     // Don't mount filesystems or start core system services in charger mode.
     std::string bootmode = GetProperty("ro.bootmode", "");
     if (bootmode == "charger") {
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 1bcc5ef..0411214 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -22,6 +22,7 @@
 #include "action.h"
 #include "action_manager.h"
 #include "action_parser.h"
+#include "builtin_arguments.h"
 #include "builtins.h"
 #include "import_parser.h"
 #include "keyword_map.h"
@@ -29,7 +30,6 @@
 #include "service.h"
 #include "service_list.h"
 #include "service_parser.h"
-#include "test_function_map.h"
 #include "util.h"
 
 namespace android {
@@ -37,14 +37,15 @@
 
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
-void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,
               const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     ActionManager am;
 
     Action::set_function_map(&test_function_map);
 
     Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -59,7 +60,7 @@
     }
 }
 
-void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+void TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map,
                   const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
@@ -75,8 +76,13 @@
 pass_test
 )init";
 
-    TestFunctionMap test_function_map;
-    test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+    auto do_pass_test = [&expect_true](const BuiltinArguments&) {
+        expect_true = true;
+        return Result<void>{};
+    };
+    BuiltinFunctionMap test_function_map = {
+            {"pass_test", {0, 0, {false, do_pass_test}}},
+    };
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
@@ -102,10 +108,24 @@
 )init";
 
     int num_executed = 0;
-    TestFunctionMap test_function_map;
-    test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
-    test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
-    test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+    auto do_execute_first = [&num_executed](const BuiltinArguments&) {
+        EXPECT_EQ(0, num_executed++);
+        return Result<void>{};
+    };
+    auto do_execute_second = [&num_executed](const BuiltinArguments&) {
+        EXPECT_EQ(1, num_executed++);
+        return Result<void>{};
+    };
+    auto do_execute_third = [&num_executed](const BuiltinArguments&) {
+        EXPECT_EQ(2, num_executed++);
+        return Result<void>{};
+    };
+
+    BuiltinFunctionMap test_function_map = {
+            {"execute_first", {0, 0, {false, do_execute_first}}},
+            {"execute_second", {0, 0, {false, do_execute_second}}},
+            {"execute_third", {0, 0, {false, do_execute_third}}},
+    };
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
@@ -126,7 +146,7 @@
 )init";
 
     ServiceList service_list;
-    TestInitText(init_script, TestFunctionMap(), {}, &service_list);
+    TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
     ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
 
     auto service = service_list.begin()->get();
@@ -185,8 +205,9 @@
         return Result<void>{};
     };
 
-    TestFunctionMap test_function_map;
-    test_function_map.Add("execute", 1, 1, false, execute_command);
+    BuiltinFunctionMap test_function_map = {
+            {"execute", {1, 1, {false, execute_command}}},
+    };
 
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
diff --git a/init/interface_utils.cpp b/init/interface_utils.cpp
new file mode 100644
index 0000000..a54860f
--- /dev/null
+++ b/init/interface_utils.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "interface_utils.h"
+
+#include <fstream>
+#include <sstream>
+
+#include <android-base/strings.h>
+#include <hidl-util/FqInstance.h>
+#include <json/json.h>
+
+using android::FqInstance;
+using android::FQName;
+using android::base::Error;
+
+namespace android {
+namespace init {
+
+namespace {
+
+std::string FQNamesToString(const std::set<FQName>& fqnames) {
+    std::set<std::string> fqname_strings;
+    for (const FQName& fqname : fqnames) {
+        fqname_strings.insert(fqname.string());
+    }
+    return android::base::Join(fqname_strings, " ");
+}
+
+}  // namespace
+
+Result<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) {
+        FqInstance fqinstance;
+        if (!fqinstance.setTo(instance)) {
+            return Error() << "Unable to parse interface instance '" << instance << "'";
+        }
+        interface_fqnames.insert(fqinstance.getFqName());
+    }
+    return CheckInterfaceInheritanceHierarchy(interface_fqnames, hierarchy);
+}
+
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<FQName>& interfaces,
+                                                const InterfaceInheritanceHierarchyMap& hierarchy) {
+    std::ostringstream error_stream;
+    for (const FQName& intf : interfaces) {
+        if (hierarchy.count(intf) == 0) {
+            error_stream << "\nInterface is not in the known set of hidl_interfaces: '"
+                         << intf.string()
+                         << "'. Please ensure the interface is spelled correctly and built "
+                         << "by a hidl_interface target.";
+            continue;
+        }
+        const std::set<FQName>& required_interfaces = hierarchy.at(intf);
+        std::set<FQName> diff;
+        std::set_difference(required_interfaces.begin(), required_interfaces.end(),
+                            interfaces.begin(), interfaces.end(),
+                            std::inserter(diff, diff.begin()));
+        if (!diff.empty()) {
+            error_stream << "\nInterface '" << intf.string() << "' requires its full inheritance "
+                         << "hierarchy to be listed in this init_rc file. Missing "
+                         << "interfaces: [" << FQNamesToString(diff) << "]";
+        }
+    }
+    const std::string& errors = error_stream.str();
+    if (!errors.empty()) {
+        return Error() << errors;
+    }
+
+    return {};
+}
+
+std::optional<std::set<FQName>> known_interfaces;
+
+void SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy) {
+    known_interfaces = std::set<FQName>();
+    for (const auto& [intf, inherited_interfaces] : hierarchy) {
+        known_interfaces->insert(intf);
+    }
+}
+
+Result<void> IsKnownInterface(const std::string& instance) {
+    FqInstance fqinstance;
+    if (!fqinstance.setTo(instance)) {
+        return Error() << "Unable to parse interface instance '" << instance << "'";
+    }
+    return IsKnownInterface(fqinstance.getFqName());
+}
+
+Result<void> IsKnownInterface(const FQName& intf) {
+    if (!known_interfaces) {
+        return Error() << "No known interfaces have been loaded.";
+    }
+    if (known_interfaces->count(intf) == 0) {
+        return Error() << "Interface is not in the known set of hidl_interfaces: '" << intf.string()
+                       << "'. Please ensure the interface is spelled correctly and built "
+                       << "by a hidl_interface target.";
+    }
+    return {};
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/interface_utils.h b/init/interface_utils.h
new file mode 100644
index 0000000..bd0c104
--- /dev/null
+++ b/init/interface_utils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+
+#include <hidl-util/FQName.h>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+using InterfaceInheritanceHierarchyMap = std::map<android::FQName, std::set<android::FQName>>;
+
+// 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.
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
+                                                const InterfaceInheritanceHierarchyMap& hierarchy);
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<android::FQName>& interfaces,
+                                                const InterfaceInheritanceHierarchyMap& hierarchy);
+
+// Saves the set of known interfaces using the provided HIDL interface
+// inheritance hierarchy.
+void SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy);
+
+// Checks if the provided interface is in the set of known interfaces. Returns
+// an empty Result if present, otherwise an Error.
+Result<void> IsKnownInterface(const std::string& instance);
+Result<void> IsKnownInterface(const FQName& intf);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/keychords.cpp b/init/keychords.cpp
index d0ca3e7..5f2682b 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -285,7 +285,7 @@
 
 void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
     epoll_ = epoll;
-    handler_ = handler;
+    handler_ = std::move(handler);
     if (entries_.size()) GeteventOpenDevice();
 }
 
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 7837bb3..d92678f 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -18,36 +18,49 @@
 
 #include <map>
 #include <string>
+#include <vector>
 
 #include "result.h"
 
 namespace android {
 namespace init {
 
-template <typename Function>
+// Every init builtin, init service option, and ueventd option has a minimum and maximum number of
+// arguments.  These must be checked both at run time for safety and also at build time for
+// correctness in host_init_verifier.  Instead of copying and pasting the boiler plate code that
+// does this check into each function, it is abstracted in KeywordMap<>.  This class maps keywords
+// to functions and checks that the number of arguments provided falls in the correct range or
+// returns an error otherwise.
+
+// Value is the return value of Find(), which is typically either a single function or a struct with
+// additional information.
+template <typename Value>
 class KeywordMap {
   public:
-    using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
-    using Map = std::map<std::string, FunctionInfo>;
+    struct MapValue {
+        size_t min_args;
+        size_t max_args;
+        Value value;
+    };
 
-    virtual ~KeywordMap() {
-    }
+    KeywordMap() {}
+    KeywordMap(std::initializer_list<std::pair<const std::string, MapValue>> init) : map_(init) {}
 
-    const Result<Function> FindFunction(const std::vector<std::string>& args) const {
+    Result<Value> Find(const std::vector<std::string>& args) const {
         if (args.empty()) return Error() << "Keyword needed, but not provided";
 
         auto& keyword = args[0];
         auto num_args = args.size() - 1;
 
-        auto function_info_it = map().find(keyword);
-        if (function_info_it == map().end()) {
+        auto result_it = map_.find(keyword);
+        if (result_it == map_.end()) {
             return Errorf("Invalid keyword '{}'", keyword);
         }
 
-        auto function_info = function_info_it->second;
+        auto result = result_it->second;
 
-        auto min_args = std::get<0>(function_info);
-        auto max_args = std::get<1>(function_info);
+        auto min_args = result.min_args;
+        auto max_args = result.max_args;
         if (min_args == max_args && num_args != min_args) {
             return Errorf("{} requires {} argument{}", keyword, min_args,
                           (min_args > 1 || min_args == 0) ? "s" : "");
@@ -63,13 +76,11 @@
             }
         }
 
-        return std::get<Function>(function_info);
+        return result.value;
     }
 
   private:
-    // Map of keyword ->
-    // (minimum number of arguments, maximum number of arguments, function pointer)
-    virtual const Map& map() const = 0;
+    std::map<std::string, MapValue> map_;
 };
 
 }  // namespace init
diff --git a/init/main.cpp b/init/main.cpp
index 2ce46ef..38bc74b 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -60,7 +60,7 @@
     if (argc > 1) {
         if (!strcmp(argv[1], "subcontext")) {
             android::base::InitLogging(argv, &android::base::KernelLogger);
-            const BuiltinFunctionMap function_map;
+            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
 
             return SubcontextMain(argc, argv, &function_map);
         }
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index b0b63c5..791a019 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -140,11 +140,11 @@
         }
     }
     free(buf);
-    for (auto entry : untouched) {
+    for (auto& entry : untouched) {
         SetMountProperty(entry, false);
         mounts_.erase(entry);
     }
-    for (auto entry : touched) {
+    for (auto& entry : touched) {
         SetMountProperty(entry, true);
         mounts_.emplace(std::move(entry));
     }
diff --git a/init/parser.cpp b/init/parser.cpp
index bbfbdc6..6ab61cb 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -37,7 +37,7 @@
 }
 
 void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
-    line_callbacks_.emplace_back(prefix, callback);
+    line_callbacks_.emplace_back(prefix, std::move(callback));
 }
 
 void Parser::ParseData(const std::string& filename, std::string* data) {
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 73787b9..baa9ad4 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -69,7 +69,7 @@
             continue;
         }
 
-        unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
+        unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
         if (fd == -1) {
             PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
             continue;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 1520c9f..3408ff3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,7 +39,6 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-#include <atomic>
 #include <map>
 #include <memory>
 #include <mutex>
@@ -53,7 +52,6 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <property_info_parser/property_info_parser.h>
 #include <property_info_serializer/property_info_serializer.h>
 #include <selinux/android.h>
@@ -61,6 +59,7 @@
 #include <selinux/selinux.h>
 
 #include "debug_ramdisk.h"
+#include "epoll.h"
 #include "init.h"
 #include "persistent_properties.h"
 #include "property_type.h"
@@ -77,7 +76,6 @@
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::Trim;
-using android::base::unique_fd;
 using android::base::WriteStringToFile;
 using android::properties::BuildTrie;
 using android::properties::ParsePropertyInfoFile;
@@ -174,13 +172,8 @@
         return PROP_ERROR_INVALID_NAME;
     }
 
-    if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
-        *error = "Property value too long";
-        return PROP_ERROR_INVALID_VALUE;
-    }
-
-    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
-        *error = "Value is not a UTF8 encoded string";
+    if (auto result = IsLegalPropertyValue(name, value); !result) {
+        *error = result.error().message();
         return PROP_ERROR_INVALID_VALUE;
     }
 
@@ -648,13 +641,14 @@
             }
 
             std::string raw_filename(fn);
-            std::string expanded_filename;
-            if (!expand_props(raw_filename, &expanded_filename)) {
-                LOG(ERROR) << "Could not expand filename '" << raw_filename << "'";
+            auto expanded_filename = ExpandProps(raw_filename);
+
+            if (!expanded_filename) {
+                LOG(ERROR) << "Could not expand filename ': " << expanded_filename.error();
                 continue;
             }
 
-            load_properties_from_file(expanded_filename.c_str(), key, properties);
+            load_properties_from_file(expanded_filename->c_str(), key, properties);
         } else {
             value = strchr(key, '=');
             if (!value) continue;
@@ -667,9 +661,9 @@
 
             if (flen > 0) {
                 if (filter[flen - 1] == '*') {
-                    if (strncmp(key, filter, flen - 1)) continue;
+                    if (strncmp(key, filter, flen - 1) != 0) continue;
                 } else {
-                    if (strcmp(key, filter)) continue;
+                    if (strcmp(key, filter) != 0) continue;
                 }
             }
 
@@ -782,10 +776,9 @@
             "brand", "device", "manufacturer", "model", "name",
     };
     const char* RO_PRODUCT_PROPS_ALLOWED_SOURCES[] = {
-            "odm", "product", "product_services", "system", "vendor",
+            "odm", "product", "system_ext", "system", "vendor",
     };
-    const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER =
-            "product,product_services,odm,vendor,system";
+    const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = "product,odm,vendor,system_ext,system";
     const std::string EMPTY = "";
 
     std::string ro_product_props_source_order =
@@ -892,6 +885,7 @@
         }
     }
     load_properties_from_file("/system/build.prop", nullptr, &properties);
+    load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
     load_properties_from_file("/vendor/default.prop", nullptr, &properties);
     load_properties_from_file("/vendor/build.prop", nullptr, &properties);
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
@@ -901,7 +895,6 @@
         load_properties_from_file("/odm/build.prop", nullptr, &properties);
     }
     load_properties_from_file("/product/build.prop", nullptr, &properties);
-    load_properties_from_file("/product_services/build.prop", nullptr, &properties);
     load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
 
     if (load_debug_prop) {
@@ -995,10 +988,11 @@
 void StartPropertyService(Epoll* epoll) {
     property_set("ro.property_service.version", "2");
 
-    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, nullptr);
-    if (property_set_fd == -1) {
-        PLOG(FATAL) << "start_property_service socket creation failed";
+    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();
     }
 
     listen(property_set_fd, 8);
@@ -1008,42 +1002,5 @@
     }
 }
 
-Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f) {
-    unique_fd reader;
-    unique_fd writer;
-    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &reader, &writer)) {
-        return ErrnoError() << "Could not create socket pair";
-    }
-
-    int result = 0;
-    std::atomic<bool> end = false;
-    auto thread = std::thread{[&f, &result, &end, &writer] {
-        result = f();
-        end = true;
-        send(writer, "1", 1, 0);
-    }};
-
-    Epoll epoll;
-    if (auto result = epoll.Open(); !result) {
-        return Error() << "Could not create epoll: " << result.error();
-    }
-    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
-        return Error() << "Could not register epoll handler for property fd: " << result.error();
-    }
-
-    // No-op function, just used to break from loop.
-    if (auto result = epoll.RegisterHandler(reader, [] {}); !result) {
-        return Error() << "Could not register epoll handler for ending thread:" << result.error();
-    }
-
-    while (!end) {
-        epoll.Wait({});
-    }
-
-    thread.join();
-
-    return result;
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index dc47b4d..7f9f844 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,11 +18,9 @@
 
 #include <sys/socket.h>
 
-#include <functional>
 #include <string>
 
 #include "epoll.h"
-#include "result.h"
 
 namespace android {
 namespace init {
@@ -39,13 +37,5 @@
 void load_persist_props();
 void StartPropertyService(Epoll* epoll);
 
-template <typename F, typename... Args>
-Result<int> CallFunctionAndHandleProperties(F&& f, Args&&... args) {
-    Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f);
-
-    auto func = [&] { return f(args...); };
-    return CallFunctionAndHandlePropertiesImpl(func);
-}
-
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index c038aff..0f4cd0d 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -56,6 +56,11 @@
 }
 
 TEST(property_service, non_utf8_value) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Skipping test, must be run as root.";
+        return;
+    }
+
     ASSERT_TRUE(SetProperty("property_service_utf8_test", "base_success"));
     EXPECT_FALSE(SetProperty("property_service_utf8_test", "\x80"));
     EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xC2\x01"));
diff --git a/init/reboot.cpp b/init/reboot.cpp
index d9d885c..b0b5b54 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -636,11 +636,9 @@
     bool run_fsck = false;
     bool command_invalid = false;
 
-    if (cmd_params.size() > 3) {
-        command_invalid = true;
-    } else if (cmd_params[0] == "shutdown") {
+    if (cmd_params[0] == "shutdown") {
         cmd = ANDROID_RB_POWEROFF;
-        if (cmd_params.size() == 2) {
+        if (cmd_params.size() >= 2) {
             if (cmd_params[1] == "userrequested") {
                 // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
                 // Run fsck once the file system is remounted in read-only mode.
@@ -671,6 +669,20 @@
                                   "bootloader_message: "
                                << err;
                 }
+            } else if (reboot_target == "recovery") {
+                bootloader_message boot = {};
+                if (std::string err; !read_bootloader_message(&boot, &err)) {
+                    LOG(ERROR) << "Failed to read bootloader message: " << err;
+                }
+                // Update the boot command field if it's empty, and preserve
+                // the other arguments in the bootloader message.
+                if (boot.command[0] == '\0') {
+                    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+                    if (std::string err; !write_bootloader_message(boot, &err)) {
+                        LOG(ERROR) << "Failed to set bootloader message: " << err;
+                        return false;
+                    }
+                }
             } else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
                        reboot_target == "fastboot") {
                 std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
@@ -686,9 +698,9 @@
                 reboot_target = "recovery";
             }
 
-            // If there is an additional parameter, pass it along
-            if ((cmd_params.size() == 3) && cmd_params[2].size()) {
-                reboot_target += "," + cmd_params[2];
+            // If there are additional parameter, pass them along
+            for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {
+                reboot_target += "," + cmd_params[i];
             }
         }
     } else {
diff --git a/init/security.cpp b/init/security.cpp
index 5d87f3c..586d0c7 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -83,7 +83,7 @@
     return {};
 }
 
-static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+static bool SetHighestAvailableOptionValue(const std::string& path, int min, int max) {
     std::ifstream inf(path, std::fstream::in);
     if (!inf) {
         LOG(ERROR) << "Cannot open for reading: " << path;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 54be086..fd42256 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -497,28 +497,33 @@
 // This function returns the Android version with which the vendor SEPolicy was compiled.
 // It is used for version checks such as whether or not vendor_init should be used
 int SelinuxGetVendorAndroidVersion() {
-    if (!IsSplitPolicyDevice()) {
-        // If this device does not split sepolicy files, it's not a Treble device and therefore,
-        // we assume it's always on the latest platform.
-        return __ANDROID_API_FUTURE__;
-    }
+    static int vendor_android_version = [] {
+        if (!IsSplitPolicyDevice()) {
+            // If this device does not split sepolicy files, it's not a Treble device and therefore,
+            // we assume it's always on the latest platform.
+            return __ANDROID_API_FUTURE__;
+        }
 
-    std::string version;
-    if (!GetVendorMappingVersion(&version)) {
-        LOG(FATAL) << "Could not read vendor SELinux version";
-    }
+        std::string version;
+        if (!GetVendorMappingVersion(&version)) {
+            LOG(FATAL) << "Could not read vendor SELinux version";
+        }
 
-    int major_version;
-    std::string major_version_str(version, 0, version.find('.'));
-    if (!ParseInt(major_version_str, &major_version)) {
-        PLOG(FATAL) << "Failed to parse the vendor sepolicy major version " << major_version_str;
-    }
+        int major_version;
+        std::string major_version_str(version, 0, version.find('.'));
+        if (!ParseInt(major_version_str, &major_version)) {
+            PLOG(FATAL) << "Failed to parse the vendor sepolicy major version "
+                        << major_version_str;
+        }
 
-    return major_version;
+        return major_version;
+    }();
+    return vendor_android_version;
 }
 
 // This function initializes SELinux then execs init to run in the init SELinux context.
 int SetupSelinux(char** argv) {
+    SetStdioToDevNull(argv);
     InitKernelLogging(argv);
 
     if (REBOOT_BOOTLOADER_ON_PANIC) {
diff --git a/init/service.cpp b/init/service.cpp
index cd08f3d..9537843 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -31,6 +31,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <cutils/sockets.h>
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
 
@@ -99,9 +100,11 @@
     expanded_args.resize(args.size());
     c_strings.push_back(const_cast<char*>(args[0].data()));
     for (std::size_t i = 1; i < args.size(); ++i) {
-        if (!expand_props(args[i], &expanded_args[i])) {
-            LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
+        auto expanded_arg = ExpandProps(args[i]);
+        if (!expanded_arg) {
+            LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error();
         }
+        expanded_args[i] = *expanded_arg;
         c_strings.push_back(expanded_args[i].data());
     }
     c_strings.push_back(nullptr);
@@ -126,7 +129,7 @@
     : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, args) {}
 
 Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
-                 const std::vector<gid_t>& supp_gids, unsigned namespace_flags,
+                 const std::vector<gid_t>& supp_gids, int namespace_flags,
                  const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
                  const std::vector<std::string>& args)
     : name_(name),
@@ -164,6 +167,15 @@
             property_set(boottime_property, std::to_string(start_ns));
         }
     }
+
+    // init.svc_debug_pid.* properties are only for tests, and should not be used
+    // on device for security checks.
+    std::string pid_property = "init.svc_debug_pid." + name_;
+    if (new_state == "running") {
+        property_set(pid_property, std::to_string(pid_));
+    } else if (new_state == "stopped") {
+        property_set(pid_property, "");
+    }
 }
 
 void Service::KillProcessGroup(int signal) {
@@ -227,9 +239,11 @@
         KillProcessGroup(SIGKILL);
     }
 
-    // Remove any descriptor resources we may have created.
-    std::for_each(descriptors_.begin(), descriptors_.end(),
-                  std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
+    // Remove any socket resources we may have created.
+    for (const auto& socket : sockets_) {
+        auto path = ANDROID_SOCKET_DIR "/" + socket.name;
+        unlink(path.c_str());
+    }
 
     for (const auto& f : reap_callbacks_) {
         f(siginfo);
@@ -300,8 +314,12 @@
     LOG(INFO) << "service " << name_;
     LOG(INFO) << "  class '" << Join(classnames_, " ") << "'";
     LOG(INFO) << "  exec " << Join(args_, " ");
-    std::for_each(descriptors_.begin(), descriptors_.end(),
-                  [] (const auto& info) { LOG(INFO) << *info; });
+    for (const auto& socket : sockets_) {
+        LOG(INFO) << "  socket " << socket.name;
+    }
+    for (const auto& file : files_) {
+        LOG(INFO) << "  file " << file.name;
+    }
 }
 
 
@@ -419,8 +437,17 @@
             setenv(key.c_str(), value.c_str(), 1);
         }
 
-        std::for_each(descriptors_.begin(), descriptors_.end(),
-                      std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
+        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();
+            }
+        }
 
         if (auto result = WritePidToFiles(&writepid_files_); !result) {
             LOG(ERROR) << "failed to write pid to files: " << result.error();
@@ -617,7 +644,8 @@
     }
 }
 
-std::unique_ptr<Service> Service::MakeTemporaryOneshotService(const std::vector<std::string>& args) {
+Result<std::unique_ptr<Service>> Service::MakeTemporaryOneshotService(
+        const std::vector<std::string>& args) {
     // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
     // SECLABEL can be a - to denote default
     std::size_t command_arg = 1;
@@ -628,13 +656,11 @@
         }
     }
     if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
-        LOG(ERROR) << "exec called with too many supplementary group ids";
-        return nullptr;
+        return Error() << "exec called with too many supplementary group ids";
     }
 
     if (command_arg >= args.size()) {
-        LOG(ERROR) << "exec called without command";
-        return nullptr;
+        return Error() << "exec called without command";
     }
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
@@ -653,8 +679,7 @@
     if (command_arg > 3) {
         uid = DecodeUid(args[2]);
         if (!uid) {
-            LOG(ERROR) << "Unable to decode UID for '" << args[2] << "': " << uid.error();
-            return nullptr;
+            return Error() << "Unable to decode UID for '" << args[2] << "': " << uid.error();
         }
     }
     Result<gid_t> gid = 0;
@@ -662,16 +687,14 @@
     if (command_arg > 4) {
         gid = DecodeUid(args[3]);
         if (!gid) {
-            LOG(ERROR) << "Unable to decode GID for '" << args[3] << "': " << gid.error();
-            return nullptr;
+            return Error() << "Unable to decode GID for '" << args[3] << "': " << gid.error();
         }
         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
         for (size_t i = 0; i < nr_supp_gids; ++i) {
             auto supp_gid = DecodeUid(args[4 + i]);
             if (!supp_gid) {
-                LOG(ERROR) << "Unable to decode GID for '" << args[4 + i]
-                           << "': " << supp_gid.error();
-                return nullptr;
+                return Error() << "Unable to decode GID for '" << args[4 + i]
+                               << "': " << supp_gid.error();
             }
             supp_gids.push_back(*supp_gid);
         }
diff --git a/init/service.h b/init/service.h
index 78f94ce..ccefc8e 100644
--- a/init/service.h
+++ b/init/service.h
@@ -31,7 +31,6 @@
 
 #include "action.h"
 #include "capabilities.h"
-#include "descriptors.h"
 #include "keyword_map.h"
 #include "parser.h"
 #include "service_utils.h"
@@ -69,11 +68,11 @@
             const std::vector<std::string>& args);
 
     Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
-            const std::vector<gid_t>& supp_gids, unsigned namespace_flags,
-            const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
-            const std::vector<std::string>& args);
+            const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
+            Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
 
-    static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
+    static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
+            const std::vector<std::string>& args);
 
     bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
     Result<void> ExecStart();
@@ -98,6 +97,7 @@
     void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
         reap_callbacks_.emplace_back(std::move(callback));
     }
+    size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
 
     static bool is_exec_service_running() { return is_exec_service_running_; }
 
@@ -109,7 +109,7 @@
     int crash_count() const { return crash_count_; }
     uid_t uid() const { return proc_attr_.uid; }
     gid_t gid() const { return proc_attr_.gid; }
-    unsigned namespace_flags() const { return namespaces_.flags; }
+    int namespace_flags() const { return namespaces_.flags; }
     const std::vector<gid_t>& supp_gids() const { return proc_attr_.supp_gids; }
     const std::string& seclabel() const { return seclabel_; }
     const std::vector<int>& keycodes() const { return keycodes_; }
@@ -152,7 +152,8 @@
 
     std::string seclabel_;
 
-    std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
+    std::vector<SocketDescriptor> sockets_;
+    std::vector<FileDescriptor> files_;
     std::vector<std::pair<std::string, std::string>> environment_vars_;
 
     Action onrestart_;  // Commands to execute on restart.
diff --git a/init/service_list.cpp b/init/service_list.cpp
index 3a48183..c51a9cf 100644
--- a/init/service_list.cpp
+++ b/init/service_list.cpp
@@ -28,6 +28,14 @@
     return instance;
 }
 
+size_t ServiceList::CheckAllCommands() {
+    size_t failures = 0;
+    for (const auto& service : services_) {
+        failures += service->CheckAllCommands();
+    }
+    return failures;
+}
+
 void ServiceList::AddService(std::unique_ptr<Service> service) {
     services_.emplace_back(std::move(service));
 }
diff --git a/init/service_list.h b/init/service_list.h
index 2136a21..ee2c702 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -30,6 +30,7 @@
 
     // Exposed for testing
     ServiceList();
+    size_t CheckAllCommands();
 
     void AddService(std::unique_ptr<Service> service);
     void RemoveService(const Service& svc);
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 33ed050..dd552fb 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -17,6 +17,11 @@
 #include "service_parser.h"
 
 #include <linux/input.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+#include <algorithm>
+#include <sstream>
 
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -25,6 +30,7 @@
 #include <system/thread_defs.h>
 
 #include "rlimit_parser.h"
+#include "service_utils.h"
 #include "util.h"
 
 #if defined(__ANDROID__)
@@ -187,9 +193,9 @@
 Result<void> ServiceParser::ParseKeycodes(std::vector<std::string>&& args) {
     auto it = args.begin() + 1;
     if (args.size() == 2 && StartsWith(args[1], "$")) {
-        std::string expanded;
-        if (!expand_props(args[1], &expanded)) {
-            return Error() << "Could not expand property '" << args[1] << "'";
+        auto expanded = ExpandProps(args[1]);
+        if (!expanded) {
+            return expanded.error();
         }
 
         // If the property is not set, it defaults to none, in which case there are no keycodes
@@ -198,7 +204,7 @@
             return {};
         }
 
-        args = Split(expanded, ",");
+        args = Split(*expanded, ",");
         it = args.begin();
     }
 
@@ -341,64 +347,100 @@
     return {};
 }
 
-template <typename T>
-Result<void> ServiceParser::AddDescriptor(std::vector<std::string>&& args) {
-    int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
-    Result<uid_t> uid = 0;
-    Result<gid_t> gid = 0;
-    std::string context = args.size() > 6 ? args[6] : "";
+// name type perm [ uid gid context ]
+Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
+    SocketDescriptor socket;
+    socket.name = std::move(args[1]);
+
+    auto types = Split(args[2], "+");
+    if (types[0] == "stream") {
+        socket.type = SOCK_STREAM;
+    } else if (types[0] == "dgram") {
+        socket.type = SOCK_DGRAM;
+    } else if (types[0] == "seqpacket") {
+        socket.type = SOCK_SEQPACKET;
+    } else {
+        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket', got '" << types[0]
+                       << "' instead.";
+    }
+
+    if (types.size() > 1) {
+        if (types.size() == 2 && types[1] == "passcred") {
+            socket.passcred = true;
+        } else {
+            return Error() << "Only 'passcred' may be used to modify the socket type";
+        }
+    }
+
+    errno = 0;
+    char* end = nullptr;
+    socket.perm = strtol(args[3].c_str(), &end, 8);
+    if (errno != 0) {
+        return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
+    }
+    if (end == args[3].c_str() || *end != '\0') {
+        errno = EINVAL;
+        return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
+    }
 
     if (args.size() > 4) {
-        uid = DecodeUid(args[4]);
+        auto uid = DecodeUid(args[4]);
         if (!uid) {
             return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
         }
+        socket.uid = *uid;
     }
 
     if (args.size() > 5) {
-        gid = DecodeUid(args[5]);
+        auto gid = DecodeUid(args[5]);
         if (!gid) {
             return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
         }
+        socket.gid = *gid;
     }
 
-    auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
+    socket.context = args.size() > 6 ? args[6] : "";
 
-    auto old = std::find_if(
-            service_->descriptors_.begin(), service_->descriptors_.end(),
-            [&descriptor](const auto& other) { return descriptor.get() == other.get(); });
+    auto old = std::find_if(service_->sockets_.begin(), service_->sockets_.end(),
+                            [&socket](const auto& other) { return socket.name == other.name; });
 
-    if (old != service_->descriptors_.end()) {
-        return Error() << "duplicate descriptor " << args[1] << " " << args[2];
+    if (old != service_->sockets_.end()) {
+        return Error() << "duplicate socket descriptor '" << socket.name << "'";
     }
 
-    service_->descriptors_.emplace_back(std::move(descriptor));
+    service_->sockets_.emplace_back(std::move(socket));
+
     return {};
 }
 
-// name type perm [ uid gid context ]
-Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
-    if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
-        !StartsWith(args[2], "seqpacket")) {
-        return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
-    }
-    return AddDescriptor<SocketInfo>(std::move(args));
-}
-
-// name type perm [ uid gid context ]
+// name type
 Result<void> ServiceParser::ParseFile(std::vector<std::string>&& args) {
     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
         return Error() << "file type must be 'r', 'w' or 'rw'";
     }
-    std::string expanded;
-    if (!expand_props(args[1], &expanded)) {
-        return Error() << "Could not expand property in file path '" << args[1] << "'";
+
+    FileDescriptor file;
+    file.type = args[2];
+
+    auto file_name = ExpandProps(args[1]);
+    if (!file_name) {
+        return Error() << "Could not expand file path ': " << file_name.error();
     }
-    args[1] = std::move(expanded);
-    if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
+    file.name = *file_name;
+    if (file.name[0] != '/' || file.name.find("../") != std::string::npos) {
         return Error() << "file name must not be relative";
     }
-    return AddDescriptor<FileInfo>(std::move(args));
+
+    auto old = std::find_if(service_->files_.begin(), service_->files_.end(),
+                            [&file](const auto& other) { return other.name == file.name; });
+
+    if (old != service_->files_.end()) {
+        return Error() << "duplicate file descriptor '" << file.name << "'";
+    }
+
+    service_->files_.emplace_back(std::move(file));
+
+    return {};
 }
 
 Result<void> ServiceParser::ParseUser(std::vector<std::string>&& args) {
@@ -421,18 +463,10 @@
     return {};
 }
 
-class ServiceParser::OptionParserMap : public KeywordMap<OptionParser> {
-  public:
-    OptionParserMap() {}
-
-  private:
-    const Map& map() const override;
-};
-
-const ServiceParser::OptionParserMap::Map& ServiceParser::OptionParserMap::map() const {
+const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     // clang-format off
-    static const Map option_parsers = {
+    static const KeywordMap<ServiceParser::OptionParser> parser_map = {
         {"capabilities",
                         {0,     kMax, &ServiceParser::ParseCapabilities}},
         {"class",       {1,     kMax, &ServiceParser::ParseClass}},
@@ -478,7 +512,7 @@
         {"writepid",    {1,     kMax, &ServiceParser::ParseWritepid}},
     };
     // clang-format on
-    return option_parsers;
+    return parser_map;
 }
 
 Result<void> ServiceParser::ParseSection(std::vector<std::string>&& args,
@@ -521,8 +555,7 @@
         return {};
     }
 
-    static const OptionParserMap parser_map;
-    auto parser = parser_map.FindFunction(args);
+    auto parser = GetParserMap().Find(args);
 
     if (!parser) return parser.error();
 
@@ -534,6 +567,14 @@
         return {};
     }
 
+    if (interface_inheritance_hierarchy_) {
+        if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy(
+                    service_->interfaces(), *interface_inheritance_hierarchy_);
+            !check_hierarchy_result) {
+            return Error() << check_hierarchy_result.error();
+        }
+    }
+
     Service* old_service = service_list_->FindService(service_->name());
     if (old_service) {
         if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 0a5b291..4729874 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -18,6 +18,7 @@
 
 #include <vector>
 
+#include "interface_utils.h"
 #include "parser.h"
 #include "service.h"
 #include "service_list.h"
@@ -28,8 +29,13 @@
 
 class ServiceParser : public SectionParser {
   public:
-    ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
-        : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
+    ServiceParser(
+            ServiceList* service_list, std::vector<Subcontext>* subcontexts,
+            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
+        : service_list_(service_list),
+          subcontexts_(subcontexts),
+          interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
+          service_(nullptr) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line) override;
     Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -38,7 +44,7 @@
 
   private:
     using OptionParser = Result<void> (ServiceParser::*)(std::vector<std::string>&& args);
-    class OptionParserMap;
+    const KeywordMap<ServiceParser::OptionParser>& GetParserMap() const;
 
     Result<void> ParseCapabilities(std::vector<std::string>&& args);
     Result<void> ParseClass(std::vector<std::string>&& args);
@@ -74,13 +80,11 @@
     Result<void> ParseWritepid(std::vector<std::string>&& args);
     Result<void> ParseUpdatable(std::vector<std::string>&& args);
 
-    template <typename T>
-    Result<void> AddDescriptor(std::vector<std::string>&& args);
-
     bool IsValidName(const std::string& name) const;
 
     ServiceList* service_list_;
     std::vector<Subcontext>* subcontexts_;
+    std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
     std::unique_ptr<Service> service_;
     std::string filename_;
 };
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 4bfaa6b..c9cc7bd 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -30,7 +30,7 @@
 
 TEST(service, pod_initialized) {
     constexpr auto memory_size = sizeof(Service);
-    alignas(alignof(Service)) char old_memory[memory_size];
+    alignas(alignof(Service)) unsigned char old_memory[memory_size];
 
     for (std::size_t i = 0; i < memory_size; ++i) {
         old_memory[i] = 0xFF;
@@ -45,7 +45,7 @@
     EXPECT_EQ(0, service_in_old_memory->crash_count());
     EXPECT_EQ(0U, service_in_old_memory->uid());
     EXPECT_EQ(0U, service_in_old_memory->gid());
-    EXPECT_EQ(0U, service_in_old_memory->namespace_flags());
+    EXPECT_EQ(0, service_in_old_memory->namespace_flags());
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory->priority());
@@ -64,7 +64,7 @@
     EXPECT_EQ(0, service_in_old_memory2->crash_count());
     EXPECT_EQ(0U, service_in_old_memory2->uid());
     EXPECT_EQ(0U, service_in_old_memory2->gid());
-    EXPECT_EQ(0U, service_in_old_memory2->namespace_flags());
+    EXPECT_EQ(0, service_in_old_memory2->namespace_flags());
     EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());
     EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());
     EXPECT_EQ(0, service_in_old_memory2->priority());
@@ -75,15 +75,15 @@
 TEST(service, make_temporary_oneshot_service_invalid_syntax) {
     std::vector<std::string> args;
     // Nothing.
-    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
 
     // No arguments to 'exec'.
     args.push_back("exec");
-    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
 
     // No command in "exec --".
     args.push_back("--");
-    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
 }
 
 TEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {
@@ -97,7 +97,7 @@
     }
     args.push_back("--");
     args.push_back("/system/bin/id");
-    ASSERT_EQ(nullptr, Service::MakeTemporaryOneshotService(args));
+    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args));
 }
 
 static void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
@@ -122,8 +122,9 @@
     }
     args.push_back("/system/bin/toybox");
     args.push_back("id");
-    auto svc = Service::MakeTemporaryOneshotService(args);
-    ASSERT_NE(nullptr, svc);
+    auto service_ret = Service::MakeTemporaryOneshotService(args);
+    ASSERT_TRUE(service_ret);
+    auto svc = std::move(*service_ret);
 
     if (seclabel) {
         ASSERT_EQ("u:r:su:s0", svc->seclabel());
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index f88ea97..836145d 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -27,9 +27,12 @@
 #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>
 
 #include "mount_namespace.h"
+#include "util.h"
 
 using android::base::GetProperty;
 using android::base::StartsWith;
@@ -120,26 +123,67 @@
 }
 
 void ZapStdio() {
-    int fd;
-    fd = open("/dev/null", O_RDWR);
+    auto fd = unique_fd{open("/dev/null", O_RDWR | O_CLOEXEC)};
     dup2(fd, 0);
     dup2(fd, 1);
     dup2(fd, 2);
-    close(fd);
 }
 
 void OpenConsole(const std::string& console) {
-    int fd = open(console.c_str(), O_RDWR);
-    if (fd == -1) fd = open("/dev/null", O_RDWR);
+    auto fd = unique_fd{open(console.c_str(), O_RDWR | O_CLOEXEC)};
+    if (fd == -1) fd.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
     ioctl(fd, TIOCSCTTY, 0);
     dup2(fd, 0);
     dup2(fd, 1);
     dup2(fd, 2);
-    close(fd);
+}
+
+void PublishDescriptor(const std::string& key, const std::string& name, int fd) {
+    std::string published_name = key + name;
+    for (auto& c : published_name) {
+        c = isalnum(c) ? c : '_';
+    }
+
+    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 {
+    const auto& socket_context = context.empty() ? global_context : context;
+    auto result = CreateSocket(name, type, passcred, perm, uid, gid, socket_context);
+    if (!result) {
+        return result.error();
+    }
+
+    PublishDescriptor(ANDROID_SOCKET_ENV_PREFIX, name, *result);
+
+    return {};
+}
+
+Result<void> FileDescriptor::CreateAndPublish() const {
+    int flags = (type == "r") ? O_RDONLY : (type == "w") ? O_WRONLY : O_RDWR;
+
+    // Make sure we do not block on open (eg: devices can chose to block on carrier detect).  Our
+    // intention is never to delay launch of a service for such a condition.  The service can
+    // perform its own blocking on carrier detect.
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK)));
+
+    if (fd < 0) {
+        return ErrnoError() << "Failed to open file '" << name << "'";
+    }
+
+    // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
+    fcntl(fd, F_SETFL, flags);
+
+    LOG(INFO) << "Opened file '" << name << "', flags " << flags;
+
+    PublishDescriptor(ANDROID_FILE_ENV_PREFIX, name, fd.release());
+
+    return {};
+}
+
 Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
     for (const auto& [nstype, path] : info.namespaces_to_enter) {
         if (auto result = EnterNamespace(nstype, path.c_str()); !result) {
diff --git a/init/service_utils.h b/init/service_utils.h
index c26b123..befce25 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -29,8 +29,27 @@
 namespace android {
 namespace init {
 
+struct SocketDescriptor {
+    std::string name;
+    int type = 0;
+    uid_t uid = 0;
+    gid_t gid = 0;
+    int perm = 0;
+    std::string context;
+    bool passcred = false;
+
+    Result<void> CreateAndPublish(const std::string& global_context) const;
+};
+
+struct FileDescriptor {
+    std::string name;
+    std::string type;
+
+    Result<void> CreateAndPublish() const;
+};
+
 struct NamespaceInfo {
-    unsigned flags;
+    int flags;
     // Pair of namespace type, path to name.
     std::vector<std::pair<int, std::string>> namespaces_to_enter;
 };
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index c9a09cd..984235d 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -73,6 +73,13 @@
                 auto exec_duration_ms =
                     std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
                 wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+            } else if (service->flags() & SVC_ONESHOT) {
+                auto exec_duration = boot_clock::now() - service->time_started();
+                auto exec_duration_ms =
+                        std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
+                                .count();
+                wait_string = StringPrintf(" oneshot service took %f seconds in background",
+                                           exec_duration_ms / 1000.0f);
             }
         } else {
             name = StringPrintf("Untracked pid %d", pid);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 02ed507..00f91d8 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -27,6 +27,7 @@
 #include <selinux/android.h>
 
 #include "action.h"
+#include "builtins.h"
 #include "util.h"
 
 #if defined(__ANDROID__)
@@ -99,7 +100,7 @@
 
 class SubcontextProcess {
   public:
-    SubcontextProcess(const KeywordFunctionMap* function_map, std::string context, int init_fd)
+    SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)
         : function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};
     void MainLoop();
 
@@ -109,7 +110,7 @@
     void ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
                     SubcontextReply* reply) const;
 
-    const KeywordFunctionMap* function_map_;
+    const BuiltinFunctionMap* function_map_;
     const std::string context_;
     const int init_fd_;
 };
@@ -122,12 +123,12 @@
         args.emplace_back(string);
     }
 
-    auto map_result = function_map_->FindFunction(args);
+    auto map_result = function_map_->Find(args);
     Result<void> result;
     if (!map_result) {
         result = Error() << "Cannot find command: " << map_result.error();
     } else {
-        result = RunBuiltinFunction(map_result->second, args, context_);
+        result = RunBuiltinFunction(map_result->function, args, context_);
     }
 
     for (const auto& [name, value] : properties_to_set) {
@@ -150,15 +151,15 @@
 void SubcontextProcess::ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
                                    SubcontextReply* reply) const {
     for (const auto& arg : expand_args_command.args()) {
-        auto expanded_prop = std::string{};
-        if (!expand_props(arg, &expanded_prop)) {
+        auto expanded_arg = ExpandProps(arg);
+        if (!expanded_arg) {
             auto* failure = reply->mutable_failure();
-            failure->set_error_string("Failed to expand '" + arg + "'");
+            failure->set_error_string(expanded_arg.error().message());
             failure->set_error_errno(0);
             return;
         } else {
             auto* expand_args_reply = reply->mutable_expand_args_reply();
-            expand_args_reply->add_expanded_args(expanded_prop);
+            expand_args_reply->add_expanded_args(*expanded_arg);
         }
     }
 }
@@ -215,7 +216,7 @@
 
 }  // namespace
 
-int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map) {
+int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map) {
     if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")";
 
     auto context = std::string(argv[2]);
@@ -246,7 +247,7 @@
 
         // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
         // in the subcontext process after we exec.
-        int child_fd = dup(subcontext_socket);
+        int child_fd = dup(subcontext_socket);  // NOLINT(android-cloexec-dup)
         if (child_fd < 0) {
             PLOG(FATAL) << "Could not dup child_fd";
         }
diff --git a/init/subcontext.h b/init/subcontext.h
index 16bd870..591521f 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -60,7 +60,7 @@
     android::base::unique_fd socket_;
 };
 
-int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
+int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
 std::vector<Subcontext>* InitializeSubcontexts();
 bool SubcontextChildReap(pid_t pid);
 void SubcontextTerminate();
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index fdbbc41..f6fee8a 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -19,8 +19,6 @@
 #include <benchmark/benchmark.h>
 #include <selinux/selinux.h>
 
-#include "test_function_map.h"
-
 namespace android {
 namespace init {
 
@@ -50,11 +48,11 @@
 
 BENCHMARK(BenchmarkSuccess);
 
-TestFunctionMap BuildTestFunctionMap() {
-    TestFunctionMap test_function_map;
-    test_function_map.Add("return_success", 0, 0, true,
-                          [](const BuiltinArguments& args) { return Result<void>{}; });
-
+BuiltinFunctionMap BuildTestFunctionMap() {
+    auto function = [](const BuiltinArguments& args) { return Result<void>{}; };
+    BuiltinFunctionMap test_function_map = {
+            {"return_success", {0, 0, {true, function}}},
+    };
     return test_function_map;
 }
 
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 55912d6..dcbff82 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -26,7 +26,6 @@
 #include <selinux/selinux.h>
 
 #include "builtin_arguments.h"
-#include "test_function_map.h"
 
 using namespace std::literals;
 
@@ -44,7 +43,7 @@
 template <typename F>
 void RunTest(F&& test_function) {
     if (getuid() != 0) {
-        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
 
@@ -167,50 +166,58 @@
         };
         auto result = subcontext.ExpandArgs(args);
         ASSERT_FALSE(result);
-        EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error().message());
+        EXPECT_EQ("unexpected end of string in '" + args[1] + "', looking for }",
+                  result.error().message());
     });
 }
 
-TestFunctionMap BuildTestFunctionMap() {
-    TestFunctionMap test_function_map;
+BuiltinFunctionMap BuildTestFunctionMap() {
     // For CheckDifferentPid
-    test_function_map.Add("return_pids_as_error", 0, 0, true,
-                          [](const BuiltinArguments& args) -> Result<void> {
-                              return Error() << getpid() << " " << getppid();
-                          });
+    auto do_return_pids_as_error = [](const BuiltinArguments& args) -> Result<void> {
+        return Error() << getpid() << " " << getppid();
+    };
 
     // For SetProp
-    test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) {
+    auto do_setprop = [](const BuiltinArguments& args) {
         android::base::SetProperty(args[1], args[2]);
         return Result<void>{};
-    });
+    };
 
     // For MultipleCommands
     // Using a shared_ptr to extend lifetime of words to both lambdas
     auto words = std::make_shared<std::vector<std::string>>();
-    test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) {
+    auto do_add_word = [words](const BuiltinArguments& args) {
         words->emplace_back(args[1]);
         return Result<void>{};
-    });
-    test_function_map.Add("return_words_as_error", 0, 0, true,
-                          [words](const BuiltinArguments& args) -> Result<void> {
-                              return Error() << Join(*words, " ");
-                          });
+    };
+    auto do_return_words_as_error = [words](const BuiltinArguments& args) -> Result<void> {
+        return Error() << Join(*words, " ");
+    };
 
     // For RecoverAfterAbort
-    test_function_map.Add("cause_log_fatal", 0, 0, true,
-                          [](const BuiltinArguments& args) -> Result<void> {
-                              return Error() << std::string(4097, 'f');
-                          });
-    test_function_map.Add(
-            "generate_sane_error", 0, 0, true,
-            [](const BuiltinArguments& args) -> Result<void> { return Error() << "Sane error!"; });
+    auto do_cause_log_fatal = [](const BuiltinArguments& args) -> Result<void> {
+        return Error() << std::string(4097, 'f');
+    };
+    auto do_generate_sane_error = [](const BuiltinArguments& args) -> Result<void> {
+        return Error() << "Sane error!";
+    };
 
     // For ContextString
-    test_function_map.Add(
-            "return_context_as_error", 0, 0, true,
-            [](const BuiltinArguments& args) -> Result<void> { return Error() << args.context; });
+    auto do_return_context_as_error = [](const BuiltinArguments& args) -> Result<void> {
+        return Error() << args.context;
+    };
 
+    // clang-format off
+    BuiltinFunctionMap test_function_map = {
+        {"return_pids_as_error",        {0,     0,      {true,  do_return_pids_as_error}}},
+        {"setprop",                     {2,     2,      {true,  do_setprop}}},
+        {"add_word",                    {1,     1,      {true,  do_add_word}}},
+        {"return_words_as_error",       {0,     0,      {true,  do_return_words_as_error}}},
+        {"cause_log_fatal",             {0,     0,      {true,  do_cause_log_fatal}}},
+        {"generate_sane_error",         {0,     0,      {true,  do_generate_sane_error}}},
+        {"return_context_as_error",     {0,     0,      {true,  do_return_context_as_error}}},
+    };
+    // clang-format on
     return test_function_map;
 }
 
diff --git a/init/test_function_map.h b/init/test_function_map.h
deleted file mode 100644
index 293f1f9..0000000
--- a/init/test_function_map.h
+++ /dev/null
@@ -1,53 +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.
- */
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include "builtin_arguments.h"
-#include "builtins.h"
-#include "keyword_map.h"
-
-namespace android {
-namespace init {
-
-class TestFunctionMap : public KeywordFunctionMap {
-  public:
-    // Helper for argument-less functions
-    using BuiltinFunctionNoArgs = std::function<void(void)>;
-    void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
-        Add(name, 0, 0, false, [function](const BuiltinArguments&) {
-            function();
-            return Result<void>{};
-        });
-    }
-
-    void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
-             bool run_in_subcontext, const BuiltinFunction function) {
-        builtin_functions_[name] =
-            make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
-    }
-
-  private:
-    Map builtin_functions_ = {};
-
-    const Map& map() const override { return builtin_functions_; }
-};
-
-}  // namespace init
-}  // namespace android
diff --git a/init/test_utils/Android.bp b/init/test_utils/Android.bp
new file mode 100644
index 0000000..1cb05b6
--- /dev/null
+++ b/init/test_utils/Android.bp
@@ -0,0 +1,27 @@
+cc_library_static {
+    name: "libinit_test_utils",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wno-unused-parameter",
+        "-Werror",
+    ],
+    srcs: [
+        "service_utils.cpp",
+    ],
+    shared_libs: [
+        "libcutils",
+        "liblog",
+        "libjsoncpp",
+        "libprotobuf-cpp-lite",
+        "libhidl-gen-utils",
+    ],
+    whole_static_libs: [
+        "libinit",
+        "libpropertyinfoparser",
+    ],
+    static_libs: [
+        "libbase",
+    ],
+    export_include_dirs: ["include"], // for tests
+}
diff --git a/init/test_utils/include/init-test-utils/service_utils.h b/init/test_utils/include/init-test-utils/service_utils.h
new file mode 100644
index 0000000..3ec61d4
--- /dev/null
+++ b/init/test_utils/include/init-test-utils/service_utils.h
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <map>
+#include <set>
+
+#include <android-base/result.h>
+#include <hidl-util/FqInstance.h>
+
+namespace android {
+namespace init {
+
+using ServiceInterfacesMap = std::map<std::string, std::set<android::FqInstance>>;
+android::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap();
+
+}  // namespace init
+}  // namespace android
diff --git a/init/test_utils/service_utils.cpp b/init/test_utils/service_utils.cpp
new file mode 100644
index 0000000..bc00702
--- /dev/null
+++ b/init/test_utils/service_utils.cpp
@@ -0,0 +1,63 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <string>
+
+#include <android-base/logging.h>
+
+#include "../parser.h"
+#include "../service.h"
+#include "../service_list.h"
+#include "../service_parser.h"
+#include "include/init-test-utils/service_utils.h"
+
+namespace android {
+namespace init {
+
+android::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap() {
+    ServiceList& service_list = ServiceList::GetInstance();
+    Parser parser;
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+    for (const auto& location : {
+                 "/init.rc",
+                 "/system/etc/init",
+                 "/system_ext/etc/init",
+                 "/product/etc/init",
+                 "/odm/etc/init",
+                 "/vendor/etc/init",
+         }) {
+        parser.ParseConfig(location);
+    }
+
+    ServiceInterfacesMap result;
+    for (const auto& service : service_list.services()) {
+        // Create an entry for all services, including services that may not
+        // have any declared interfaces.
+        result[service->name()] = std::set<android::FqInstance>();
+        for (const auto& intf : service->interfaces()) {
+            android::FqInstance fqInstance;
+            if (!fqInstance.setTo(intf)) {
+                return android::base::Error() << "Unable to parse interface: '" << intf << "'";
+            }
+            result[service->name()].insert(fqInstance);
+        }
+    }
+    return result;
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/tokenizer_test.cpp b/init/tokenizer_test.cpp
index acfc7c7..6b31683 100644
--- a/init/tokenizer_test.cpp
+++ b/init/tokenizer_test.cpp
@@ -46,6 +46,7 @@
                 return;
             case T_NEWLINE:
                 tokens.emplace_back(std::move(current_line));
+                current_line.clear();
                 break;
             case T_TEXT:
                 current_line.emplace_back(state.text);
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 62cd2be..ac633776 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -131,7 +131,7 @@
                                                        const ListenerCallback& callback) const {
     int dfd = dirfd(d);
 
-    int fd = openat(dfd, "uevent", O_WRONLY);
+    int fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
     if (fd >= 0) {
         write(fd, "add\n", 4);
         close(fd);
@@ -146,7 +146,7 @@
     while ((de = readdir(d)) != nullptr) {
         if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;
 
-        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
         if (fd < 0) continue;
 
         std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 3b9de0f..8b2cf62 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -146,7 +146,7 @@
 
 void ColdBoot::RegenerateUevents() {
     uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
-        uevent_queue_.emplace_back(std::move(uevent));
+        uevent_queue_.emplace_back(uevent);
         return ListenerAction::kContinue;
     });
 }
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 25bab93..8ee0cce 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -176,21 +176,14 @@
 
 Result<void> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {
     using OptionParser = Result<void> (SubsystemParser::*)(std::vector<std::string> && args);
+    // clang-format off
+    static const KeywordMap<OptionParser> parser_map = {
+        {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
+        {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
+    };
+    // clang-format on
 
-    static class OptionParserMap : public KeywordMap<OptionParser> {
-      private:
-        const Map& map() const override {
-            // clang-format off
-            static const Map option_parsers = {
-                {"devname",     {1,     1,      &SubsystemParser::ParseDevName}},
-                {"dirname",     {1,     1,      &SubsystemParser::ParseDirName}},
-            };
-            // clang-format on
-            return option_parsers;
-        }
-    } parser_map;
-
-    auto parser = parser_map.FindFunction(args);
+    auto parser = parser_map.Find(args);
 
     if (!parser) return Error() << parser.error();
 
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index bfdc28e..2d7d2f8 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -68,7 +68,7 @@
 
 TEST(ueventd, setegid_IsPerThread) {
     if (getuid() != 0) {
-        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
 
@@ -92,11 +92,11 @@
 
 TEST(ueventd, setfscreatecon_IsPerThread) {
     if (getuid() != 0) {
-        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
     if (!is_selinux_enabled() || security_getenforce() == 1) {
-        GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
+        GTEST_SKIP() << "Skipping test, SELinux must be enabled and in permissive mode.";
         return;
     }
 
@@ -127,7 +127,7 @@
 
 TEST(ueventd, selabel_lookup_MultiThreaded) {
     if (getuid() != 0) {
-        GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+        GTEST_SKIP() << "Skipping test, must be run as root.";
         return;
     }
 
diff --git a/init/util.cpp b/init/util.cpp
index 2b34242..0532375 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -34,19 +34,25 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include <selinux/android.h>
 
 #if defined(__ANDROID__)
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+
 #include "reboot_utils.h"
 #include "selabel.h"
+#include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
 
 using android::base::boot_clock;
+using android::base::StartsWith;
 using namespace std::literals::string_literals;
 
 namespace android {
@@ -77,32 +83,28 @@
  * daemon. We communicate the file descriptor's value via the environment
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
-int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
-                 const char* socketcon) {
-    if (socketcon) {
-        if (setsockcreatecon(socketcon) == -1) {
-            PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
-            return -1;
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
+                         gid_t gid, const std::string& socketcon) {
+    if (!socketcon.empty()) {
+        if (setsockcreatecon(socketcon.c_str()) == -1) {
+            return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";
         }
     }
 
     android::base::unique_fd fd(socket(PF_UNIX, type, 0));
     if (fd < 0) {
-        PLOG(ERROR) << "Failed to open socket '" << name << "'";
-        return -1;
+        return ErrnoError() << "Failed to open socket '" << name << "'";
     }
 
-    if (socketcon) setsockcreatecon(NULL);
+    if (!socketcon.empty()) setsockcreatecon(nullptr);
 
     struct sockaddr_un addr;
     memset(&addr, 0 , sizeof(addr));
     addr.sun_family = AF_UNIX;
-    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
-             name);
+    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR "/%s", name.c_str());
 
     if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {
-        PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
-        return -1;
+        return ErrnoError() << "Failed to unlink old socket '" << name << "'";
     }
 
     std::string secontext;
@@ -113,8 +115,7 @@
     if (passcred) {
         int on = 1;
         if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
-            PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
-            return -1;
+            return ErrnoError() << "Failed to set SO_PASSCRED '" << name << "'";
         }
     }
 
@@ -125,19 +126,18 @@
         setfscreatecon(nullptr);
     }
 
+    auto guard = android::base::make_scope_guard([&addr] { unlink(addr.sun_path); });
+
     if (ret) {
         errno = savederrno;
-        PLOG(ERROR) << "Failed to bind socket '" << name << "'";
-        goto out_unlink;
+        return ErrnoError() << "Failed to bind socket '" << name << "'";
     }
 
     if (lchown(addr.sun_path, uid, gid)) {
-        PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
-        goto out_unlink;
+        return ErrnoError() << "Failed to lchown socket '" << addr.sun_path << "'";
     }
     if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
-        PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
-        goto out_unlink;
+        return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";
     }
 
     LOG(INFO) << "Created socket '" << addr.sun_path << "'"
@@ -145,11 +145,8 @@
               << ", user " << uid
               << ", group " << gid;
 
+    guard.Disable();
     return fd.release();
-
-out_unlink:
-    unlink(addr.sun_path);
-    return -1;
 }
 
 Result<std::string> ReadFile(const std::string& path) {
@@ -275,12 +272,10 @@
     return S_ISDIR(info.st_mode);
 }
 
-bool expand_props(const std::string& src, std::string* dst) {
+Result<std::string> ExpandProps(const std::string& src) {
     const char* src_ptr = src.c_str();
 
-    if (!dst) {
-        return false;
-    }
+    std::string dst;
 
     /* - variables can either be $x.y or ${x.y}, in case they are only part
      *   of the string.
@@ -294,19 +289,19 @@
 
         c = strchr(src_ptr, '$');
         if (!c) {
-            dst->append(src_ptr);
-            return true;
+            dst.append(src_ptr);
+            return dst;
         }
 
-        dst->append(src_ptr, c);
+        dst.append(src_ptr, c);
         c++;
 
         if (*c == '$') {
-            dst->push_back(*(c++));
+            dst.push_back(*(c++));
             src_ptr = c;
             continue;
         } else if (*c == '\0') {
-            return true;
+            return dst;
         }
 
         std::string prop_name;
@@ -316,8 +311,7 @@
             const char* end = strchr(c, '}');
             if (!end) {
                 // failed to find closing brace, abort.
-                LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
-                return false;
+                return Error() << "unexpected end of string in '" << src << "', looking for }";
             }
             prop_name = std::string(c, end);
             c = end + 1;
@@ -328,29 +322,34 @@
             }
         } else {
             prop_name = c;
-            LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
+            if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
+                return Error() << "using deprecated syntax for specifying property '" << c
+                               << "', use ${name} instead";
+            } else {
+                LOG(ERROR) << "using deprecated syntax for specifying property '" << c
+                           << "', use ${name} instead";
+            }
             c += prop_name.size();
         }
 
         if (prop_name.empty()) {
-            LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
-            return false;
+            return Error() << "invalid zero-length property name in '" << src << "'";
         }
 
         std::string prop_val = android::base::GetProperty(prop_name, "");
         if (prop_val.empty()) {
             if (def_val.empty()) {
-                LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
-                return false;
+                return Error() << "property '" << prop_name << "' doesn't exist while expanding '"
+                               << src << "'";
             }
             prop_val = def_val;
         }
 
-        dst->append(prop_val);
+        dst.append(prop_val);
         src_ptr = c;
     }
 
-    return true;
+    return dst;
 }
 
 static std::string init_android_dt_dir() {
@@ -422,6 +421,58 @@
     return true;
 }
 
+Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value) {
+    if (value.size() >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
+        return Error() << "Property value too long";
+    }
+
+    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+        return Error() << "Value is not a UTF8 encoded string";
+    }
+
+    return {};
+}
+
+Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
+        const std::vector<std::string>& args) {
+    struct flag_type {
+        const char* name;
+        int value;
+    };
+    static const flag_type flags[] = {
+            {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
+            {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
+            {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
+            {0, 0}};
+
+    int flag = 0;
+    std::vector<std::string> paths;
+
+    bool in_flags = true;
+    for (size_t i = 1; i < args.size(); ++i) {
+        if (android::base::StartsWith(args[i], "--")) {
+            if (!in_flags) {
+                return Error() << "flags must precede paths";
+            }
+            bool found = false;
+            for (size_t j = 0; flags[j].name; ++j) {
+                if (args[i] == flags[j].name) {
+                    flag |= flags[j].value;
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                return Error() << "bad flag " << args[i];
+            }
+        } else {
+            in_flags = false;
+            paths.emplace_back(args[i]);
+        }
+    }
+    return std::pair(flag, paths);
+}
+
 static void InitAborter(const char* abort_message) {
     // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to
     // simply abort instead of trying to reboot the system.
@@ -450,7 +501,7 @@
 // SetStdioToDevNull() must be called again in second stage init.
 void SetStdioToDevNull(char** argv) {
     // Make stdin/stdout/stderr all point to /dev/null.
-    int fd = open("/dev/null", O_RDWR);
+    int fd = open("/dev/null", O_RDWR);  // NOLINT(android-cloexec-open)
     if (fd == -1) {
         int saved_errno = errno;
         android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
diff --git a/init/util.h b/init/util.h
index 1929cb5..4cccefe 100644
--- a/init/util.h
+++ b/init/util.h
@@ -14,32 +14,28 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_UTIL_H_
-#define _INIT_UTIL_H_
+#pragma once
 
 #include <sys/stat.h>
 #include <sys/types.h>
 
 #include <chrono>
 #include <functional>
-#include <ostream>
 #include <string>
 
 #include <android-base/chrono_utils.h>
-#include <selinux/label.h>
 
 #include "result.h"
 
 using android::base::boot_clock;
-using namespace std::chrono_literals;
 
 namespace android {
 namespace init {
 
 static const char kColdBootDoneProp[] = "ro.cold_boot_done";
 
-int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
-                 const char* socketcon);
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
+                         gid_t gid, const std::string& socketcon);
 
 Result<std::string> ReadFile(const std::string& path);
 Result<void> WriteFile(const std::string& path, const std::string& content);
@@ -52,7 +48,7 @@
                            const std::function<void(const std::string&, const std::string&, bool)>&);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
-bool expand_props(const std::string& src, std::string* dst);
+Result<std::string> ExpandProps(const std::string& src);
 
 // Returns the platform's Android DT directory as specified in the kernel cmdline.
 // If the platform does not configure a custom DT path, returns the standard one (based in procfs).
@@ -62,11 +58,13 @@
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
 
 bool IsLegalPropertyName(const std::string& name);
+Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
+
+Result<std::pair<int, std::vector<std::string>>> ParseRestorecon(
+        const std::vector<std::string>& args);
 
 void SetStdioToDevNull(char** argv);
 void InitKernelLogging(char** argv);
 bool IsRecoveryMode();
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 9ece847..565f2c3 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -97,7 +97,6 @@
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
         },
     },
-    whole_static_libs: ["libdemangle"],
 }
 
 cc_test_library {
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 71980d7..3e050ab 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -28,13 +28,13 @@
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
-#include <demangle.h>
-
 #include "BacktraceLog.h"
 #include "UnwindStack.h"
 
 using android::base::StringPrintf;
 
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
 //-------------------------------------------------------------------------
 // Backtrace functions.
 //-------------------------------------------------------------------------
@@ -63,7 +63,14 @@
   if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
     return "";
   }
-  return demangle(GetFunctionNameRaw(pc, offset).c_str());
+  std::string name(GetFunctionNameRaw(pc, offset));
+  char* demangled_name = __cxa_demangle(name.c_str(), nullptr, nullptr, nullptr);
+  if (demangled_name != nullptr) {
+    name = demangled_name;
+    free(demangled_name);
+    return name;
+  }
+  return name;
 }
 
 bool Backtrace::VerifyReadWordArgs(uint64_t ptr, word_t* out_value) {
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index a128623..624711f 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -24,7 +24,6 @@
 #include <string>
 
 #include <backtrace/Backtrace.h>
-#include <demangle.h>
 #include <unwindstack/Elf.h>
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
@@ -41,6 +40,8 @@
 #include "UnwindStack.h"
 #include "UnwindStackMap.h"
 
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
 bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
                        std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
                        std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
@@ -115,7 +116,13 @@
     back_frame->pc = frame->pc;
     back_frame->sp = frame->sp;
 
-    back_frame->func_name = demangle(frame->function_name.c_str());
+    char* demangled_name = __cxa_demangle(frame->function_name.c_str(), nullptr, nullptr, nullptr);
+    if (demangled_name != nullptr) {
+      back_frame->func_name = demangled_name;
+      free(demangled_name);
+    } else {
+      back_frame->func_name = frame->function_name;
+    }
     back_frame->func_offset = frame->function_offset;
 
     back_frame->map.name = frame->map_name;
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 319a73a..b9420d4 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -67,8 +67,6 @@
         "native_handle.cpp",
         "record_stream.cpp",
         "sockets.cpp",
-        "strdup16to8.cpp",
-        "strdup8to16.cpp",
         "strlcpy.c",
         "threads.cpp",
     ],
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 897a169..b29638c 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -106,7 +106,7 @@
 // oem/ file-system since the intent is to provide support for customized
 // portions of a separate vendor.img or oem.img.  Has to remain open so that
 // customization can also land on /system/vendor, /system/oem, /system/odm,
-// /system/product or /system/product_services.
+// /system/product or /system/system_ext.
 //
 // We expect build-time checking or filtering when constructing the associated
 // fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
@@ -118,15 +118,12 @@
 static const char odm_conf_file[] = "/odm/etc/fs_config_files";
 static const char product_conf_dir[] = "/product/etc/fs_config_dirs";
 static const char product_conf_file[] = "/product/etc/fs_config_files";
-static const char product_services_conf_dir[] = "/product_services/etc/fs_config_dirs";
-static const char product_services_conf_file[] = "/product_services/etc/fs_config_files";
+static const char system_ext_conf_dir[] = "/system_ext/etc/fs_config_dirs";
+static const char system_ext_conf_file[] = "/system_ext/etc/fs_config_files";
 static const char* conf[][2] = {
-        {sys_conf_file, sys_conf_dir},
-        {ven_conf_file, ven_conf_dir},
-        {oem_conf_file, oem_conf_dir},
-        {odm_conf_file, odm_conf_dir},
-        {product_conf_file, product_conf_dir},
-        {product_services_conf_file, product_services_conf_dir},
+        {sys_conf_file, sys_conf_dir},         {ven_conf_file, ven_conf_dir},
+        {oem_conf_file, oem_conf_dir},         {odm_conf_file, odm_conf_dir},
+        {product_conf_file, product_conf_dir}, {system_ext_conf_file, system_ext_conf_dir},
 };
 
 // Do not use android_files to grant Linux capabilities.  Use ambient capabilities in their
@@ -158,9 +155,9 @@
     { 00600, AID_ROOT,      AID_ROOT,      0, "product/build.prop" },
     { 00444, AID_ROOT,      AID_ROOT,      0, product_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, product_conf_file + 1 },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "product_services/build.prop" },
-    { 00444, AID_ROOT,      AID_ROOT,      0, product_services_conf_dir + 1 },
-    { 00444, AID_ROOT,      AID_ROOT,      0, product_services_conf_file + 1 },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "system_ext/build.prop" },
+    { 00444, AID_ROOT,      AID_ROOT,      0, system_ext_conf_dir + 1 },
+    { 00444, AID_ROOT,      AID_ROOT,      0, system_ext_conf_file + 1 },
     { 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" },
@@ -248,9 +245,9 @@
 }
 
 // if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>",
-// "product_services/<stuff>" or "vendor/<stuff>"
+// "system_ext/<stuff>" or "vendor/<stuff>"
 static bool is_partition(const std::string& path) {
-    static const char* partitions[] = {"odm/", "oem/", "product/", "product_services/", "vendor/"};
+    static const char* partitions[] = {"odm/", "oem/", "product/", "system_ext/", "vendor/"};
     for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
         if (StartsWith(path, partitions[i])) return true;
     }
@@ -285,10 +282,8 @@
     if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true;
 
     // Check match between logical partition's files and patterns.
-    static constexpr const char* kLogicalPartitions[] = {"system/product/",
-                                                         "system/product_services/",
-                                                         "system/vendor/",
-                                                         "vendor/odm/"};
+    static constexpr const char* kLogicalPartitions[] = {"system/product/", "system/system_ext/",
+                                                         "system/vendor/", "vendor/odm/"};
     for (auto& logical_partition : kLogicalPartitions) {
         if (StartsWith(input, logical_partition)) {
             std::string input_in_partition = input.substr(input.find('/') + 1);
diff --git a/libcutils/include/cutils/jstring.h b/libcutils/include/cutils/jstring.h
deleted file mode 100644
index a342608..0000000
--- a/libcutils/include/cutils/jstring.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CUTILS_STRING16_H
-#define __CUTILS_STRING16_H
-
-#include <stdint.h>
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if __STDC_VERSION__ < 201112L && __cplusplus < 201103L
-  typedef uint16_t char16_t;
-#endif
-  // otherwise char16_t is a keyword with the right semantics
-
-extern char * strndup16to8 (const char16_t* s, size_t n);
-extern size_t strnlen16to8 (const char16_t* s, size_t n);
-extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n);
-
-extern char16_t * strdup8to16 (const char* s, size_t *out_len);
-extern size_t strlen8to16 (const char* utf8Str);
-extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len);
-extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length,
-    size_t *out_len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __CUTILS_STRING16_H */
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 63c3793..ae9dab5 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -28,17 +28,10 @@
  *   mediadrm
  * Whose friendly names do not match the #define statements.
  *
- * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
- * can be used to define reserved OEM ranges used for sanity checks
- * during the build process. The rules are, they must end with START/END
- * The proper convention is incrementing a number like so:
- * AID_OEM_RESERVED_START
- * AID_OEM_RESERVED_1_START
- * AID_OEM_RESERVED_2_START
- * ...
- * The same applies to the END.
- * They are not required to be in order, but must not overlap each other and
- * must define a START and END'ing range. START must be smaller than END.
+ * This file must only be used for platform (Google managed, and submitted through AOSP), AIDs.  3rd
+ * party AIDs must be added via config.fs, which will place them in the corresponding partition's
+ * passwd and group files.  There are ranges in this file reserved for AIDs for each 3rd party
+ * partition, from which the system reads passwd and group files.
  */
 
 #ifndef _ANDROID_FILESYSTEM_CONFIG_H_
@@ -141,8 +134,12 @@
 #define AID_CACHE 2001 /* cache access */
 #define AID_DIAG 2002  /* access to diagnostic resources */
 
-/* The range 2900-2999 is reserved for OEM, and must never be
- * used here */
+/* The range 2900-2999 is reserved for the vendor partition */
+/* Note that the two 'OEM' ranges pre-dated the vendor partition, so they take the legacy 'OEM'
+ * name. Additionally, they pre-dated passwd/group files, so there are users and groups named oem_#
+ * created automatically for all values in these ranges.  If there is a user/group in a passwd/group
+ * file corresponding to this range, both the oem_# and user/group names will resolve to the same
+ * value. */
 #define AID_OEM_RESERVED_START 2900
 #define AID_OEM_RESERVED_END 2999
 
@@ -159,10 +156,26 @@
 #define AID_WAKELOCK 3010     /* Allow system wakelock read/write access */
 #define AID_UHID 3011         /* Allow read/write to /dev/uhid node */
 
-/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
+/* The range 5000-5999 is also reserved for vendor partition. */
 #define AID_OEM_RESERVED_2_START 5000
 #define AID_OEM_RESERVED_2_END 5999
 
+/* The range 6000-6499 is reserved for the system partition. */
+#define AID_SYSTEM_RESERVED_START 6000
+#define AID_SYSTEM_RESERVED_END 6499
+
+/* The range 6500-6999 is reserved for the odm partition. */
+#define AID_ODM_RESERVED_START 6500
+#define AID_ODM_RESERVED_END 6999
+
+/* The range 7000-7499 is reserved for the product partition. */
+#define AID_PRODUCT_RESERVED_START 7000
+#define AID_PRODUCT_RESERVED_END 7499
+
+/* The range 7500-7999 is reserved for the system_ext partition. */
+#define AID_SYSTEM_EXT_RESERVED_START 7500
+#define AID_SYSTEM_EXT_RESERVED_END 7999
+
 #define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
 #define AID_MISC 9998      /* access to misc storage */
 #define AID_NOBODY 9999
diff --git a/libcutils/include_vndk/cutils/jstring.h b/libcutils/include_vndk/cutils/jstring.h
deleted file mode 120000
index f3fd546..0000000
--- a/libcutils/include_vndk/cutils/jstring.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/cutils/jstring.h
\ No newline at end of file
diff --git a/libcutils/strdup16to8.cpp b/libcutils/strdup16to8.cpp
deleted file mode 100644
index d89181e..0000000
--- a/libcutils/strdup16to8.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/* libs/cutils/strdup16to8.c
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-#include <cutils/jstring.h>
-
-#include <assert.h>
-#include <limits.h>  /* for SIZE_MAX */
-#include <stdlib.h>
-
-
-/**
- * Given a UTF-16 string, compute the length of the corresponding UTF-8
- * string in bytes.
- */
-extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
-{
-    size_t utf8Len = 0;
-
-    /* A small note on integer overflow. The result can
-     * potentially be as big as 3*len, which will overflow
-     * for len > SIZE_MAX/3.
-     *
-     * Moreover, the result of a strnlen16to8 is typically used
-     * to allocate a destination buffer to strncpy16to8 which
-     * requires one more byte to terminate the UTF-8 copy, and
-     * this is generally done by careless users by incrementing
-     * the result without checking for integer overflows, e.g.:
-     *
-     *   dst = malloc(strnlen16to8(utf16,len)+1)
-     *
-     * Due to this, the following code will try to detect
-     * overflows, and never return more than (SIZE_MAX-1)
-     * when it detects one. A careless user will try to malloc
-     * SIZE_MAX bytes, which will return NULL which can at least
-     * be detected appropriately.
-     *
-     * As far as I know, this function is only used by strndup16(),
-     * but better be safe than sorry.
-     */
-
-    /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
-     */
-    if (len < (SIZE_MAX-1)/3) {
-        while (len != 0) {
-            len--;
-            unsigned int uic = *utf16Str++;
-
-            if (uic > 0x07ff)
-                utf8Len += 3;
-            else if (uic > 0x7f || uic == 0)
-                utf8Len += 2;
-            else
-                utf8Len++;
-        }
-        return utf8Len;
-    }
-
-    /* The slower but paranoid version */
-    while (len != 0) {
-        len--;
-        unsigned int  uic     = *utf16Str++;
-        size_t        utf8Cur = utf8Len;
-
-        if (uic > 0x07ff)
-            utf8Len += 3;
-        else if (uic > 0x7f || uic == 0)
-            utf8Len += 2;
-        else
-            utf8Len++;
-
-        if (utf8Len < utf8Cur) /* overflow detected */
-            return SIZE_MAX-1;
-    }
-
-    /* don't return SIZE_MAX to avoid common user bug */
-    if (utf8Len == SIZE_MAX)
-        utf8Len = SIZE_MAX-1;
-
-    return utf8Len;
-}
-
-
-/**
- * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string.
- *
- * This basically means: embedded \0's in the UTF-16 string are encoded
- * as "0xc0 0x80"
- *
- * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
- * not just "len".
- *
- * Please note, a terminated \0 is always added, so your result will always
- * be "strlen16to8() + 1" bytes long.
- */
-extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
-{
-    char* utf8cur = utf8Str;
-
-    /* Note on overflows: We assume the user did check the result of
-     * strnlen16to8() properly or at a minimum checked the result of
-     * its malloc(SIZE_MAX) in case of overflow.
-     */
-    while (len != 0) {
-        len--;
-        unsigned int uic = *utf16Str++;
-
-        if (uic > 0x07ff) {
-            *utf8cur++ = (uic >> 12) | 0xe0;
-            *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80;
-            *utf8cur++ = (uic & 0x3f) | 0x80;
-        } else if (uic > 0x7f || uic == 0) {
-            *utf8cur++ = (uic >> 6) | 0xc0;
-            *utf8cur++ = (uic & 0x3f) | 0x80;
-        } else {
-            *utf8cur++ = uic;
-
-            if (uic == 0) {
-                break;
-            }
-        }
-    }
-
-   *utf8cur = '\0';
-
-   return utf8Str;
-}
-
-/**
- * Convert a UTF-16 string to UTF-8.
- *
- */
-char * strndup16to8 (const char16_t* s, size_t n)
-{
-    if (s == NULL) {
-        return NULL;
-    }
-
-    size_t len = strnlen16to8(s, n);
-
-    /* We are paranoid, and we check for SIZE_MAX-1
-     * too since it is an overflow value for our
-     * strnlen16to8 implementation.
-     */
-    if (len >= SIZE_MAX-1)
-        return NULL;
-
-    char* ret = static_cast<char*>(malloc(len + 1));
-    if (ret == NULL)
-        return NULL;
-
-    strncpy16to8 (ret, s, n);
-
-    return ret;
-}
diff --git a/libcutils/strdup8to16.cpp b/libcutils/strdup8to16.cpp
deleted file mode 100644
index d1e51b9..0000000
--- a/libcutils/strdup8to16.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/* libs/cutils/strdup8to16.c
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-#include <cutils/jstring.h>
-
-#include <assert.h>
-#include <limits.h>
-#include <stdlib.h>
-
-/* See http://www.unicode.org/reports/tr22/ for discussion
- * on invalid sequences
- */
-
-#define UTF16_REPLACEMENT_CHAR 0xfffd
-
-/* Clever trick from Dianne that returns 1-4 depending on leading bit sequence*/
-#define UTF8_SEQ_LENGTH(ch) (((0xe5000000 >> (((ch) >> 3) & 0x1e)) & 3) + 1)
-
-/* note: macro expands to multiple lines */
-#define UTF8_SHIFT_AND_MASK(unicode, byte)  \
-            (unicode)<<=6; (unicode) |= (0x3f & (byte));
-
-#define UNICODE_UPPER_LIMIT 0x10fffd    
-
-/**
- * out_len is an out parameter (which may not be null) containing the
- * length of the UTF-16 string (which may contain embedded \0's)
- */
-
-extern char16_t * strdup8to16 (const char* s, size_t *out_len)
-{
-    char16_t *ret;
-    size_t len;
-
-    if (s == NULL) return NULL;
-
-    len = strlen8to16(s);
-
-    // fail on overflow
-    if (len && SIZE_MAX/len < sizeof(char16_t))
-        return NULL;
-
-    // no plus-one here. UTF-16 strings are not null terminated
-    ret = (char16_t *) malloc (sizeof(char16_t) * len);
-
-    return strcpy8to16 (ret, s, out_len);
-}
-
-/**
- * Like "strlen", but for strings encoded with Java's modified UTF-8.
- *
- * The value returned is the number of UTF-16 characters required
- * to represent this string.
- */
-extern size_t strlen8to16 (const char* utf8Str)
-{
-    size_t len = 0;
-    int ic;
-    int expected = 0;
-
-    while ((ic = *utf8Str++) != '\0') {
-        /* bytes that start 0? or 11 are lead bytes and count as characters.*/
-        /* bytes that start 10 are extention bytes and are not counted */
-         
-        if ((ic & 0xc0) == 0x80) {
-            /* count the 0x80 extention bytes. if we have more than
-             * expected, then start counting them because strcpy8to16
-             * will insert UTF16_REPLACEMENT_CHAR's
-             */
-            expected--;
-            if (expected < 0) {
-                len++;
-            }
-        } else {
-            len++;
-            expected = UTF8_SEQ_LENGTH(ic) - 1;
-
-            /* this will result in a surrogate pair */
-            if (expected == 3) {
-                len++;
-            }
-        }
-    }
-
-    return len;
-}
-
-
-
-/*
- * Retrieve the next UTF-32 character from a UTF-8 string.
- *
- * Stops at inner \0's
- *
- * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered
- *
- * Advances "*pUtf8Ptr" to the start of the next character.
- */
-static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr)
-{
-    uint32_t ret;
-    int seq_len;
-    int i;
-
-    /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/
-    static const unsigned char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07};
-
-    /* Bytes that start with bits "10" are not leading characters. */
-    if (((**pUtf8Ptr) & 0xc0) == 0x80) {
-        (*pUtf8Ptr)++;
-        return UTF16_REPLACEMENT_CHAR;
-    }
-
-    /* note we tolerate invalid leader 11111xxx here */    
-    seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr);
-
-    ret = (**pUtf8Ptr) & leaderMask [seq_len - 1];
-
-    if (**pUtf8Ptr == '\0') return ret;
-
-    (*pUtf8Ptr)++;
-    for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) {
-        if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR;
-        if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR;
-
-        UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr);
-    }
-
-    return ret;
-}
-
-
-/**
- * out_len is an out parameter (which may not be null) containing the
- * length of the UTF-16 string (which may contain embedded \0's)
- */
-
-extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str, 
-                                       size_t *out_len)
-{   
-    char16_t *dest = utf16Str;
-
-    while (*utf8Str != '\0') {
-        uint32_t ret;
-
-        ret = getUtf32FromUtf8(&utf8Str);
-
-        if (ret <= 0xffff) {
-            *dest++ = (char16_t) ret;
-        } else if (ret <= UNICODE_UPPER_LIMIT)  {
-            /* Create surrogate pairs */
-            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
-
-            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
-            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
-        } else {
-            *dest++ = UTF16_REPLACEMENT_CHAR;
-        }
-    }
-
-    *out_len = dest - utf16Str;
-
-    return utf16Str;
-}
-
-/**
- * length is the number of characters in the UTF-8 string.
- * out_len is an out parameter (which may not be null) containing the
- * length of the UTF-16 string (which may contain embedded \0's)
- */
-
-extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str,
-                                       int length, size_t *out_len)
-{
-    /* TODO: Share more of this code with the method above. Only 2 lines changed. */
-    
-    char16_t *dest = utf16Str;
-
-    const char *end = utf8Str + length; /* This line */
-    while (utf8Str < end) {             /* and this line changed. */
-        uint32_t ret;
-
-        ret = getUtf32FromUtf8(&utf8Str);
-
-        if (ret <= 0xffff) {
-            *dest++ = (char16_t) ret;
-        } else if (ret <= UNICODE_UPPER_LIMIT)  {
-            /* Create surrogate pairs */
-            /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */
-
-            *dest++ = 0xd800 | ((ret - 0x10000) >> 10);
-            *dest++ = 0xdc00 | ((ret - 0x10000) &  0x3ff);
-        } else {
-            *dest++ = UTF16_REPLACEMENT_CHAR;
-        }
-    }
-
-    *out_len = dest - utf16Str;
-
-    return utf16Str;
-}
diff --git a/libion/OWNERS b/libion/OWNERS
new file mode 100644
index 0000000..143ad2d
--- /dev/null
+++ b/libion/OWNERS
@@ -0,0 +1,2 @@
+sspatil@google.com
+hridya@google.com
diff --git a/libion/ion.c b/libion/ion.c
index b8de5a4..1ecfc78 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -152,6 +152,8 @@
     ion_user_handle_t handle;
     int ret;
 
+    if (!handle_fd) return -EINVAL;
+
     if (!ion_is_legacy(fd)) {
         struct ion_new_allocation_data data = {
             .len = len,
@@ -201,6 +203,7 @@
     int ret;
     struct ion_heap_query query;
 
+    if (!cnt) return -EINVAL;
     memset(&query, 0, sizeof(query));
 
     ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
diff --git a/libion/tests/Android.bp b/libion/tests/Android.bp
index b3fcb3b..d3b4688 100644
--- a/libion/tests/Android.bp
+++ b/libion/tests/Android.bp
@@ -18,18 +18,15 @@
     name: "ion-unit-tests",
     cflags: [
         "-g",
-        "-Wall",
-        "-Werror",
         "-Wno-missing-field-initializers",
     ],
     shared_libs: ["libion"],
     srcs: [
-        "ion_test_fixture.cpp",
         "allocate_test.cpp",
-        "formerly_valid_handle_test.cpp",
-        "invalid_values_test.cpp",
-        "map_test.cpp",
-        "device_test.cpp",
         "exit_test.cpp",
+    	"heap_query.cpp",
+        "invalid_values_test.cpp",
+        "ion_test_fixture.cpp",
+        "map_test.cpp",
     ],
 }
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index 3c4524e..5ed01bb 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,95 +14,106 @@
  * limitations under the License.
  */
 
-#include <memory>
 #include <sys/mman.h>
+#include <memory>
 
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
 #include "ion_test_fixture.h"
 
-class Allocate : public IonAllHeapsTest {
-};
+class Allocate : public IonTest {};
 
-TEST_F(Allocate, Allocate)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, Allocate) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
-            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
-            ASSERT_TRUE(handle != 0);
-            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+            int fd;
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &fd));
+            ASSERT_TRUE(fd != 0);
+            ASSERT_EQ(close(fd), 0);  // free the buffer
         }
     }
 }
 
-TEST_F(Allocate, AllocateCached)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, AllocateCached) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
-            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &handle));
-            ASSERT_TRUE(handle != 0);
-            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+            int fd;
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), ION_FLAG_CACHED, &fd));
+            ASSERT_TRUE(fd != 0);
+            ASSERT_EQ(close(fd), 0);  // free the buffer
         }
     }
 }
 
-TEST_F(Allocate, AllocateCachedNeedsSync)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, AllocateCachedNeedsSync) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
-            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED_NEEDS_SYNC, &handle));
-            ASSERT_TRUE(handle != 0);
-            ASSERT_EQ(0, ion_free(m_ionFd, handle));
+            int fd;
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                      ION_FLAG_CACHED_NEEDS_SYNC, &fd));
+            ASSERT_TRUE(fd != 0);
+            ASSERT_EQ(close(fd), 0);  // free the buffer
         }
     }
 }
 
-TEST_F(Allocate, RepeatedAllocate)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Allocate, RepeatedAllocate) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
+            int fd;
 
             for (unsigned int i = 0; i < 1024; i++) {
                 SCOPED_TRACE(::testing::Message() << "iteration " << i);
-                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
-                ASSERT_TRUE(handle != 0);
-                ASSERT_EQ(0, ion_free(m_ionFd, handle));
+                ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &fd));
+                ASSERT_TRUE(fd != 0);
+                ASSERT_EQ(close(fd), 0);  // free the buffer
             }
         }
     }
 }
 
-TEST_F(Allocate, Zeroed)
-{
+TEST_F(Allocate, Large) {
+    for (const auto& heap : ion_heaps) {
+        SCOPED_TRACE(::testing::Message()
+                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+        int fd;
+        ASSERT_EQ(-ENOMEM,
+                  ion_alloc_fd(ionfd, 3UL * 1024 * 1024 * 1024, 0, (1 << heap.heap_id), 0, &fd));
+    }
+}
+
+// Make sure all heaps always return zeroed pages
+TEST_F(Allocate, Zeroed) {
     auto zeroes_ptr = std::make_unique<char[]>(4096);
 
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+    for (const auto& heap : ion_heaps) {
+        SCOPED_TRACE(::testing::Message()
+                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
         int fds[16];
         for (unsigned int i = 0; i < 16; i++) {
             int map_fd = -1;
 
-            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, 4096, 0, (1 << heap.heap_id), 0, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void *ptr = NULL;
+            void* ptr = NULL;
             ptr = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
 
@@ -116,13 +127,13 @@
             ASSERT_EQ(0, close(fds[i]));
         }
 
-        int newIonFd = ion_open();
+        int new_ionfd = ion_open();
         int map_fd = -1;
 
-        ASSERT_EQ(0, ion_alloc_fd(newIonFd, 4096, 0, heapMask, 0, &map_fd));
+        ASSERT_EQ(0, ion_alloc_fd(new_ionfd, 4096, 0, (1 << heap.heap_id), 0, &map_fd));
         ASSERT_GE(map_fd, 0);
 
-        void *ptr = NULL;
+        void* ptr = NULL;
         ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
         ASSERT_TRUE(ptr != NULL);
 
@@ -130,14 +141,6 @@
 
         ASSERT_EQ(0, munmap(ptr, 4096));
         ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Allocate, Large)
-{
-    for (unsigned int heapMask : m_allHeaps) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        ion_user_handle_t handle = 0;
-        ASSERT_EQ(-ENOMEM, ion_alloc(m_ionFd, 3UL*1024*1024*1024, 0, heapMask, 0, &handle));
+        ASSERT_EQ(0, ion_close(new_ionfd));
     }
 }
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
deleted file mode 100644
index eb3f7b6..0000000
--- a/libion/tests/device_test.cpp
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fcntl.h>
-#include <memory>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <linux/ion_test.h>
-
-#include <gtest/gtest.h>
-
-#include <ion/ion.h>
-
-#include "ion_test_fixture.h"
-
-#define ALIGN(x,y) (((x) + ((y) - 1)) & ~((y) - 1))
-
-class Device : public IonAllHeapsTest {
- public:
-    virtual void SetUp();
-    virtual void TearDown();
-    int m_deviceFd;
-    void readDMA(int fd, void *buf, size_t size);
-    void writeDMA(int fd, void *buf, size_t size);
-    void readKernel(int fd, void *buf, size_t size);
-    void writeKernel(int fd, void *buf, size_t size);
-    void blowCache();
-    void dirtyCache(void *ptr, size_t size);
-};
-
-void Device::SetUp()
-{
-    IonAllHeapsTest::SetUp();
-    m_deviceFd = open("/dev/ion-test", O_RDONLY);
-    ASSERT_GE(m_deviceFd, 0);
-}
-
-void Device::TearDown()
-{
-    ASSERT_EQ(0, close(m_deviceFd));
-    IonAllHeapsTest::TearDown();
-}
-
-void Device::readDMA(int fd, void *buf, size_t size)
-{
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
-    struct ion_test_rw_data ion_test_rw_data = {
-            .ptr = (uint64_t)buf,
-            .offset = 0,
-            .size = size,
-            .write = 0,
-    };
-
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::writeDMA(int fd, void *buf, size_t size)
-{
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
-    struct ion_test_rw_data ion_test_rw_data = {
-            .ptr = (uint64_t)buf,
-            .offset = 0,
-            .size = size,
-            .write = 1,
-    };
-
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_DMA_MAPPING, &ion_test_rw_data));
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::readKernel(int fd, void *buf, size_t size)
-{
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
-    struct ion_test_rw_data ion_test_rw_data = {
-            .ptr = (uint64_t)buf,
-            .offset = 0,
-            .size = size,
-            .write = 0,
-    };
-
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::writeKernel(int fd, void *buf, size_t size)
-{
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, fd));
-    struct ion_test_rw_data ion_test_rw_data = {
-            .ptr = (uint64_t)buf,
-            .offset = 0,
-            .size = size,
-            .write = 1,
-    };
-
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_KERNEL_MAPPING, &ion_test_rw_data));
-    ASSERT_EQ(0, ioctl(m_deviceFd, ION_IOC_TEST_SET_FD, -1));
-}
-
-void Device::blowCache()
-{
-    const size_t bigger_than_cache = 8*1024*1024;
-    void *buf1 = malloc(bigger_than_cache);
-    void *buf2 = malloc(bigger_than_cache);
-    memset(buf1, 0xaa, bigger_than_cache);
-    memcpy(buf2, buf1, bigger_than_cache);
-    free(buf1);
-    free(buf2);
-}
-
-void Device::dirtyCache(void *ptr, size_t size)
-{
-    /* try to dirty cache lines */
-    for (size_t i = size-1; i > 0; i--) {
-        ((volatile char *)ptr)[i];
-        ((char *)ptr)[i] = i;
-    }
-}
-
-TEST_F(Device, KernelReadCached)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        ((char*)buf)[4096] = 0x12;
-        readKernel(map_fd, buf, 4096);
-        ASSERT_EQ(((char*)buf)[4096], 0x12);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, KernelWriteCached)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeKernel(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAReadCached)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        readDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAWriteCached)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, KernelReadCachedNeedsSync)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        ((char*)buf)[4096] = 0x12;
-        readKernel(map_fd, buf, 4096);
-        ASSERT_EQ(((char*)buf)[4096], 0x12);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, KernelWriteCachedNeedsSync)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeKernel(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAReadCachedNeedsSync)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        ion_sync_fd(m_ionFd, map_fd);
-
-        readDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAWriteCachedNeedsSync)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeDMA(map_fd, buf, 4096);
-
-        ion_sync_fd(m_ionFd, map_fd);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-TEST_F(Device, KernelRead)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = 0;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        ((char*)buf)[4096] = 0x12;
-        readKernel(map_fd, buf, 4096);
-        ASSERT_EQ(((char*)buf)[4096], 0x12);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, KernelWrite)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = 0;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeKernel(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMARead)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = 0;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        for (int i = 0; i < 4096; i++)
-            ((char *)ptr)[i] = i;
-
-        readDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)buf)[i]);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, DMAWrite)
-{
-    auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
-    void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
-
-    for (int i = 0; i < 4096; i++)
-        ((char *)buf)[i] = i;
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = 0;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        writeDMA(map_fd, buf, 4096);
-
-        for (int i = 0; i < 4096; i++)
-            ASSERT_EQ((char)i, ((char *)ptr)[i]) << i;
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
-
-TEST_F(Device, IsCached)
-{
-    auto buf_ptr = std::make_unique<char[]>(4096);
-    void *buf = buf_ptr.get();
-
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        int map_fd = -1;
-        unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
-
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, flags, &map_fd));
-        ASSERT_GE(map_fd, 0);
-
-        void *ptr;
-        ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-        ASSERT_TRUE(ptr != NULL);
-
-        dirtyCache(ptr, 4096);
-
-        readDMA(map_fd, buf, 4096);
-
-        bool same = true;
-        for (int i = 4096-16; i >= 0; i -= 16)
-            if (((char *)buf)[i] != i)
-                same = false;
-        ASSERT_FALSE(same);
-
-        ASSERT_EQ(0, munmap(ptr, 4096));
-        ASSERT_EQ(0, close(map_fd));
-    }
-}
diff --git a/libion/tests/exit_test.cpp b/libion/tests/exit_test.cpp
index cdd3e27..f312389 100644
--- a/libion/tests/exit_test.cpp
+++ b/libion/tests/exit_test.cpp
@@ -22,206 +22,206 @@
 
 #include "ion_test_fixture.h"
 
-class Exit : public IonAllHeapsTest {
-};
+class Exit : public IonTest {};
 
-TEST_F(Exit, WithAlloc)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithAllocFd) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                ion_user_handle_t handle = 0;
+            EXPECT_EXIT(
+                    {
+                        int handle_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
-                ASSERT_TRUE(handle != 0);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        ASSERT_EQ(0,
+                                  ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &handle_fd));
+                        ASSERT_NE(-1, handle_fd);
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithAllocFd)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int handle_fd = -1;
-
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
-                ASSERT_NE(-1, handle_fd);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
-        }
-    }
-}
-
-TEST_F(Exit, WithRepeatedAllocFd)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithRepeatedAllocFd) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
             for (unsigned int i = 0; i < 1024; i++) {
-                SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+                SCOPED_TRACE(::testing::Message()
+                             << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
                 SCOPED_TRACE(::testing::Message() << "size " << size);
-                ASSERT_EXIT({
-                    int handle_fd = -1;
+                ASSERT_EXIT(
+                        {
+                            int handle_fd = -1;
 
-                    ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &handle_fd));
-                    ASSERT_NE(-1, handle_fd);
-                    exit(0);
-                }, ::testing::ExitedWithCode(0), "")
-                        << "failed on heap " << heapMask
-                        << " and size " << size
-                        << " on iteration " << i;
+                            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0,
+                                                      &handle_fd));
+                            ASSERT_NE(-1, handle_fd);
+                            exit(0);
+                        },
+                        ::testing::ExitedWithCode(0), "")
+                        << "failed on heap " << heap.name << ":" << heap.type << ":" << heap.heap_id
+                        << " and size " << size << " on iteration " << i;
             }
         }
     }
 }
 
-
-TEST_F(Exit, WithMapping)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithMapping) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
-                ASSERT_GE(map_fd, 0);
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
+                        ASSERT_GE(map_fd, 0);
 
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
-        }
-    }
-
-}
-
-TEST_F(Exit, WithPartialMapping)
-{
-    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
-
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
-                ASSERT_GE(map_fd, 0);
-
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-
-                ASSERT_EQ(0, munmap(ptr, size / 2));
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithMappingCached)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithPartialMapping) {
+    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
-                ASSERT_GE(map_fd, 0);
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
+                        ASSERT_GE(map_fd, 0);
 
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
-        }
-    }
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
 
-}
-
-TEST_F(Exit, WithPartialMappingCached)
-{
-    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
-
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED, &map_fd));
-                ASSERT_GE(map_fd, 0);
-
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-
-                ASSERT_EQ(0, munmap(ptr, size / 2));
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        ASSERT_EQ(0, munmap(ptr, size / 2));
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
 }
 
-TEST_F(Exit, WithMappingNeedsSync)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithMappingCached) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
-                ASSERT_GE(map_fd, 0);
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                                  ION_FLAG_CACHED, &map_fd));
+                        ASSERT_GE(map_fd, 0);
 
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
-
 }
 
-TEST_F(Exit, WithPartialMappingNeedsSync)
-{
-    static const size_t allocationSizes[] = {64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Exit, WithPartialMappingCached) {
+    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
-            EXPECT_EXIT({
-                int map_fd = -1;
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
 
-                ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC, &map_fd));
-                ASSERT_GE(map_fd, 0);
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                                  ION_FLAG_CACHED, &map_fd));
+                        ASSERT_GE(map_fd, 0);
 
-                void *ptr;
-                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
-                ASSERT_TRUE(ptr != NULL);
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
 
-                ASSERT_EQ(0, munmap(ptr, size / 2));
-                exit(0);
-            }, ::testing::ExitedWithCode(0), "");
+                        ASSERT_EQ(0, munmap(ptr, size / 2));
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithMappingNeedsSync) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
+
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                                  ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
+                                                  &map_fd));
+                        ASSERT_GE(map_fd, 0);
+
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
+        }
+    }
+}
+
+TEST_F(Exit, WithPartialMappingNeedsSync) {
+    static const size_t allocationSizes[] = {64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
+        for (size_t size : allocationSizes) {
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+            SCOPED_TRACE(::testing::Message() << "size " << size);
+            EXPECT_EXIT(
+                    {
+                        int map_fd = -1;
+
+                        ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id),
+                                                  ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
+                                                  &map_fd));
+                        ASSERT_GE(map_fd, 0);
+
+                        void* ptr;
+                        ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+                        ASSERT_TRUE(ptr != NULL);
+
+                        ASSERT_EQ(0, munmap(ptr, size / 2));
+                        exit(0);
+                    },
+                    ::testing::ExitedWithCode(0), "");
         }
     }
 }
diff --git a/libion/tests/formerly_valid_handle_test.cpp b/libion/tests/formerly_valid_handle_test.cpp
deleted file mode 100644
index 01ab8f3..0000000
--- a/libion/tests/formerly_valid_handle_test.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sys/mman.h>
-
-#include <gtest/gtest.h>
-
-#include <ion/ion.h>
-
-#include "ion_test_fixture.h"
-
-class FormerlyValidHandle : public IonTest {
- public:
-    virtual void SetUp();
-    virtual void TearDown();
-    ion_user_handle_t m_handle;
-};
-
-void FormerlyValidHandle::SetUp()
-{
-    IonTest::SetUp();
-    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, 1/* ion_env->m_firstHeap */, 0, &m_handle));
-    ASSERT_TRUE(m_handle != 0);
-    ASSERT_EQ(0, ion_free(m_ionFd, m_handle));
-}
-
-void FormerlyValidHandle::TearDown()
-{
-    m_handle = 0;
-}
-
-TEST_F(FormerlyValidHandle, free)
-{
-	ASSERT_EQ(-EINVAL, ion_free(m_ionFd, m_handle));
-}
-
-TEST_F(FormerlyValidHandle, map)
-{
-    int map_fd;
-    unsigned char *ptr;
-
-    ASSERT_EQ(-EINVAL, ion_map(m_ionFd, m_handle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-}
-
-TEST_F(FormerlyValidHandle, share)
-{
-    int share_fd;
-
-    ASSERT_EQ(-EINVAL, ion_share(m_ionFd, m_handle, &share_fd));
-}
diff --git a/fs_mgr/libfiemap_writer/utility.h b/libion/tests/heap_query.cpp
similarity index 61%
copy from fs_mgr/libfiemap_writer/utility.h
copy to libion/tests/heap_query.cpp
index 2d418da..bad3bbf 100644
--- a/fs_mgr/libfiemap_writer/utility.h
+++ b/libion/tests/heap_query.cpp
@@ -14,17 +14,14 @@
  * limitations under the License.
  */
 
-#pragma once
+#include <gtest/gtest.h>
+#include "ion_test_fixture.h"
 
-#include <string>
+class HeapQuery : public IonTest {};
 
-namespace android {
-namespace fiemap_writer {
+TEST_F(HeapQuery, AtleastOneHeap) {
+    ASSERT_GT(ion_heaps.size(), 0);
+}
 
-// Given a file that will be created, determine the maximum size its containing
-// filesystem allows. Note this is a theoretical maximum size; free space is
-// ignored entirely.
-uint64_t DetermineMaximumFileSize(const std::string& file_path);
-
-}  // namespace fiemap_writer
-}  // namespace android
+// TODO: Check if we expect some of the default
+// heap types to be present on all devices.
diff --git a/libion/tests/invalid_values_test.cpp b/libion/tests/invalid_values_test.cpp
index 77fea17..48fcd72 100644
--- a/libion/tests/invalid_values_test.cpp
+++ b/libion/tests/invalid_values_test.cpp
@@ -16,171 +16,71 @@
 
 #include <sys/mman.h>
 
+#include <memory>
+#include <vector>
+
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
-
 #include "ion_test_fixture.h"
 
-class InvalidValues : public IonAllHeapsTest {
- public:
-    virtual void SetUp();
-    virtual void TearDown();
-    ion_user_handle_t m_validHandle;
-    int m_validShareFd;
-    ion_user_handle_t const m_badHandle = -1;
-};
+class InvalidValues : public IonTest {};
 
-void InvalidValues::SetUp()
-{
-    IonAllHeapsTest::SetUp();
-    ASSERT_EQ(0, ion_alloc(m_ionFd, 4096, 0, m_firstHeap, 0, &m_validHandle))
-      << m_ionFd << " " << m_firstHeap;
-    ASSERT_TRUE(m_validHandle != 0);
-    ASSERT_EQ(0, ion_share(m_ionFd, m_validHandle, &m_validShareFd));
-}
-
-void InvalidValues::TearDown()
-{
-    ASSERT_EQ(0, ion_free(m_ionFd, m_validHandle));
-    ASSERT_EQ(0, close(m_validShareFd));
-    m_validHandle = 0;
-    IonAllHeapsTest::TearDown();
-}
-
-TEST_F(InvalidValues, ion_close)
-{
+TEST_F(InvalidValues, ion_close) {
     EXPECT_EQ(-EBADF, ion_close(-1));
 }
 
-TEST_F(InvalidValues, ion_alloc)
-{
-    ion_user_handle_t handle;
-    /* invalid ion_fd */
-    int ret = ion_alloc(0, 4096, 0, m_firstHeap, 0, &handle);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion_fd */
-    EXPECT_EQ(-EBADF, ion_alloc(-1, 4096, 0, m_firstHeap, 0, &handle));
-    /* no heaps */
-    EXPECT_EQ(-ENODEV, ion_alloc(m_ionFd, 4096, 0, 0, 0, &handle));
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        /* zero size */
-        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 0, 0, heapMask, 0, &handle));
-        /* too large size */
-        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, -1, 0, heapMask, 0, &handle));
-        /* bad alignment */
-        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, -1, heapMask, 0, &handle));
-        /* NULL handle */
-        EXPECT_EQ(-EINVAL, ion_alloc(m_ionFd, 4096, 0, heapMask, 0, NULL));
-    }
-}
-
-TEST_F(InvalidValues, ion_alloc_fd)
-{
+TEST_F(InvalidValues, ion_alloc_fd) {
     int fd;
-    /* invalid ion_fd */
-    int ret = ion_alloc_fd(0, 4096, 0, m_firstHeap, 0, &fd);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion_fd */
-    EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, m_firstHeap, 0, &fd));
-    /* no heaps */
-    EXPECT_EQ(-ENODEV, ion_alloc_fd(m_ionFd, 4096, 0, 0, 0, &fd));
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-        /* zero size */
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 0, 0, heapMask, 0, &fd));
-        /* too large size */
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, -1, 0, heapMask, 0, &fd));
-        /* bad alignment */
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, -1, heapMask, 0, &fd));
-        /* NULL handle */
-        EXPECT_EQ(-EINVAL, ion_alloc_fd(m_ionFd, 4096, 0, heapMask, 0, NULL));
+    // no heaps
+    EXPECT_EQ(-ENODEV, ion_alloc_fd(ionfd, 4096, 0, 0, 0, &fd));
+    for (const auto& heap : ion_heaps) {
+        // invalid ion_fd
+        int ret = ion_alloc_fd(0, 4096, 0, (1 << heap.heap_id), 0, &fd);
+        EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
+        // invalid ion_fd
+        EXPECT_EQ(-EBADF, ion_alloc_fd(-1, 4096, 0, (1 << heap.heap_id), 0, &fd));
+        SCOPED_TRACE(::testing::Message()
+                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
+        // zero size
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 0, 0, (1 << heap.heap_id), 0, &fd));
+        // too large size
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, -1, 0, (1 << heap.heap_id), 0, &fd));
+        // bad alignment
+        // TODO: Current userspace and kernel code completely ignores alignment. So this
+        // test is going to fail. We need to completely remove alignment from the API.
+        // All memory by default is always page aligned. OR actually pass the alignment
+        // down into the kernel and make kernel respect the alignment.
+        // EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 4096, -1, (1 << heap.heap_id), 0, &fd));
+
+        // NULL fd
+        EXPECT_EQ(-EINVAL, ion_alloc_fd(ionfd, 4096, 0, (1 << heap.heap_id), 0, nullptr));
     }
 }
 
-TEST_F(InvalidValues, ion_free)
-{
-    /* invalid ion fd */
-    int ret = ion_free(0, m_validHandle);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_free(-1, m_validHandle));
-    /* zero handle */
-    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, 0));
-    /* bad handle */
-    EXPECT_EQ(-EINVAL, ion_free(m_ionFd, m_badHandle));
+TEST_F(InvalidValues, ion_query_heap_cnt) {
+    // NULL count
+    EXPECT_EQ(-EINVAL, ion_query_heap_cnt(ionfd, nullptr));
+
+    int heap_count;
+    // bad fd
+    EXPECT_EQ(-EBADF, ion_query_heap_cnt(-1, &heap_count));
 }
 
-TEST_F(InvalidValues, ion_map)
-{
-    int map_fd;
-    unsigned char *ptr;
+TEST_F(InvalidValues, ion_query_get_heaps) {
+    int heap_count;
+    ASSERT_EQ(0, ion_query_heap_cnt(ionfd, &heap_count));
+    ASSERT_GT(heap_count, 0);
 
-    /* invalid ion fd */
-    int ret = ion_map(0, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_map(-1, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-    /* zero handle */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, 0, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-    /* bad handle */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_badHandle, 4096, PROT_READ, 0, 0, &ptr, &map_fd));
-    /* zero length */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 0, PROT_READ, 0, 0, &ptr, &map_fd));
-    /* bad prot */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, -1, 0, 0, &ptr, &map_fd));
-    /* bad offset */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, -1, &ptr, &map_fd));
-    /* NULL ptr */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, NULL, &map_fd));
-    /* NULL map_fd */
-    EXPECT_EQ(-EINVAL, ion_map(m_ionFd, m_validHandle, 4096, PROT_READ, 0, 0, &ptr, NULL));
-}
+    // nullptr buffers, still returns success but without
+    // the ion_heap_data.
+    EXPECT_EQ(0, ion_query_get_heaps(ionfd, heap_count, nullptr));
 
-TEST_F(InvalidValues, ion_share)
-{
-    int share_fd;
+    std::unique_ptr<struct ion_heap_data[]> heaps =
+            std::make_unique<struct ion_heap_data[]>(heap_count);
+    // bad fd
+    EXPECT_EQ(-EBADF, ion_query_get_heaps(-1, heap_count, heaps.get()));
 
-    /* invalid ion fd */
-    int ret = ion_share(0, m_validHandle, &share_fd);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_share(-1, m_validHandle, &share_fd));
-    /* zero handle */
-    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, 0, &share_fd));
-    /* bad handle */
-    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_badHandle, &share_fd));
-    /* NULL share_fd */
-    EXPECT_EQ(-EINVAL, ion_share(m_ionFd, m_validHandle, NULL));
-}
-
-TEST_F(InvalidValues, ion_import)
-{
-    ion_user_handle_t handle;
-
-    /* invalid ion fd */
-    int ret = ion_import(0, m_validShareFd, &handle);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_import(-1, m_validShareFd, &handle));
-    /* bad share_fd */
-    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, 0, &handle));
-    /* invalid share_fd */
-    EXPECT_EQ(-EBADF, ion_import(m_ionFd, -1, &handle));
-    /* NULL handle */
-    EXPECT_EQ(-EINVAL, ion_import(m_ionFd, m_validShareFd, NULL));
-}
-
-TEST_F(InvalidValues, ion_sync_fd)
-{
-    /* invalid ion fd */
-    int ret = ion_sync_fd(0, m_validShareFd);
-    EXPECT_TRUE(ret == -EINVAL || ret == -ENOTTY);
-    /* invalid ion fd */
-    EXPECT_EQ(-EBADF, ion_sync_fd(-1, m_validShareFd));
-    /* bad share_fd */
-    EXPECT_EQ(-EINVAL, ion_sync_fd(m_ionFd, 0));
-    /* invalid share_fd */
-    EXPECT_EQ(-EBADF, ion_sync_fd(m_ionFd, -1));
+    // invalid heap data pointer
+    EXPECT_EQ(-EFAULT, ion_query_get_heaps(ionfd, heap_count, reinterpret_cast<void*>(0xdeadf00d)));
 }
diff --git a/libion/tests/ion_4.12.h b/libion/tests/ion_4.12.h
new file mode 100644
index 0000000..614510c
--- /dev/null
+++ b/libion/tests/ion_4.12.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ ***   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.cpp b/libion/tests/ion_test_fixture.cpp
index e20c730..935fe5c 100644
--- a/libion/tests/ion_test_fixture.cpp
+++ b/libion/tests/ion_test_fixture.cpp
@@ -15,59 +15,26 @@
  */
 
 #include <gtest/gtest.h>
-
 #include <ion/ion.h>
 
 #include "ion_test_fixture.h"
 
-IonTest::IonTest() : m_ionFd(-1)
-{
-}
+IonTest::IonTest() : ionfd(-1), ion_heaps() {}
 
 void IonTest::SetUp() {
-    m_ionFd = ion_open();
-    ASSERT_GE(m_ionFd, 0);
+    ionfd = ion_open();
+    ASSERT_GE(ionfd, 0);
+
+    int heap_count;
+    int ret = ion_query_heap_cnt(ionfd, &heap_count);
+    ASSERT_EQ(ret, 0);
+    ASSERT_GT(heap_count, 0);
+
+    ion_heaps.resize(heap_count, {});
+    ret = ion_query_get_heaps(ionfd, heap_count, ion_heaps.data());
+    ASSERT_EQ(ret, 0);
 }
 
 void IonTest::TearDown() {
-    ion_close(m_ionFd);
-}
-
-IonAllHeapsTest::IonAllHeapsTest() :
-        m_firstHeap(0),
-        m_lastHeap(0),
-        m_allHeaps()
-{
-}
-
-void IonAllHeapsTest::SetUp() {
-    int fd = ion_open();
-    ASSERT_GE(fd, 0);
-
-    for (int i = 1; i != 0; i <<= 1) {
-        ion_user_handle_t handle = 0;
-        int ret;
-        ret = ion_alloc(fd, 4096, 0, i, 0, &handle);
-        if (ret == 0 && handle != 0) {
-            ion_free(fd, handle);
-            if (!m_firstHeap) {
-                m_firstHeap = i;
-            }
-            m_lastHeap = i;
-            m_allHeaps.push_back(i);
-        } else {
-            ASSERT_EQ(-ENODEV, ret);
-        }
-    }
-    ion_close(fd);
-
-    EXPECT_NE(0U, m_firstHeap);
-    EXPECT_NE(0U, m_lastHeap);
-
-    RecordProperty("Heaps", m_allHeaps.size());
-    IonTest::SetUp();
-}
-
-void IonAllHeapsTest::TearDown() {
-    IonTest::TearDown();
+    ion_close(ionfd);
 }
diff --git a/libion/tests/ion_test_fixture.h b/libion/tests/ion_test_fixture.h
index 4098214..4f254b8 100644
--- a/libion/tests/ion_test_fixture.h
+++ b/libion/tests/ion_test_fixture.h
@@ -18,29 +18,19 @@
 #define ION_TEST_FIXTURE_H_
 
 #include <gtest/gtest.h>
+#include <vector>
+#include "ion_4.12.h"
 
 using ::testing::Test;
 
 class IonTest : public virtual Test {
- public:
+  public:
     IonTest();
-	virtual ~IonTest() {};
-	virtual void SetUp();
-	virtual void TearDown();
-	int m_ionFd;
-};
-
-class IonAllHeapsTest : public IonTest {
- public:
-    IonAllHeapsTest();
-    virtual ~IonAllHeapsTest() {};
+    virtual ~IonTest(){};
     virtual void SetUp();
     virtual void TearDown();
-
-    unsigned int m_firstHeap;
-    unsigned int m_lastHeap;
-
-    std::vector<unsigned int> m_allHeaps;
+    int ionfd;
+    std::vector<struct ion_heap_data> ion_heaps;
 };
 
 #endif /* ION_TEST_FIXTURE_H_ */
diff --git a/libion/tests/map_test.cpp b/libion/tests/map_test.cpp
index c006dc8..f1b47b7 100644
--- a/libion/tests/map_test.cpp
+++ b/libion/tests/map_test.cpp
@@ -15,61 +15,30 @@
  */
 
 #include <sys/mman.h>
+#include <unistd.h>
 
 #include <gtest/gtest.h>
 
 #include <ion/ion.h>
-
 #include "ion_test_fixture.h"
 
-class Map : public IonAllHeapsTest {
-};
+class Map : public IonTest {};
 
-TEST_F(Map, MapHandle)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapFd) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
-            SCOPED_TRACE(::testing::Message() << "size " << size);
-            ion_user_handle_t handle = 0;
-
-            ASSERT_EQ(0, ion_alloc(m_ionFd, size, 0, heapMask, 0, &handle));
-            ASSERT_TRUE(handle != 0);
-
-            int map_fd = -1;
-            unsigned char *ptr = NULL;
-            ASSERT_EQ(0, ion_map(m_ionFd, handle, size, PROT_READ | PROT_WRITE, MAP_SHARED, 0, &ptr, &map_fd));
-            ASSERT_TRUE(ptr != NULL);
-            ASSERT_GE(map_fd, 0);
-
-            ASSERT_EQ(0, close(map_fd));
-
-            ASSERT_EQ(0, ion_free(m_ionFd, handle));
-
-            memset(ptr, 0xaa, size);
-
-            ASSERT_EQ(0, munmap(ptr, size));
-        }
-    }
-}
-
-TEST_F(Map, MapFd)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
-        for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
 
-            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, 0, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), 0, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void *ptr;
+            void* ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
-
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
@@ -79,53 +48,51 @@
     }
 }
 
-TEST_F(Map, MapOffset)
-{
-    for (unsigned int heapMask : m_allHeaps) {
-        SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+TEST_F(Map, MapOffset) {
+    for (const auto& heap : ion_heaps) {
+        SCOPED_TRACE(::testing::Message()
+                     << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
         int map_fd = -1;
 
-        ASSERT_EQ(0, ion_alloc_fd(m_ionFd, PAGE_SIZE * 2, 0, heapMask, 0, &map_fd));
+        ASSERT_EQ(0, ion_alloc_fd(ionfd, getpagesize() * 2, 0, (1 << heap.heap_id), 0, &map_fd));
         ASSERT_GE(map_fd, 0);
 
-        unsigned char *ptr;
-        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
+        unsigned char* ptr;
+        ptr = (unsigned char*)mmap(NULL, getpagesize() * 2, PROT_READ | PROT_WRITE, MAP_SHARED,
+                                   map_fd, 0);
         ASSERT_TRUE(ptr != NULL);
 
-        memset(ptr, 0, PAGE_SIZE);
-        memset(ptr + PAGE_SIZE, 0xaa, PAGE_SIZE);
+        memset(ptr, 0, getpagesize());
+        memset(ptr + getpagesize(), 0xaa, getpagesize());
 
-        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE * 2));
+        ASSERT_EQ(0, munmap(ptr, getpagesize() * 2));
 
-        ptr = (unsigned char *)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, PAGE_SIZE);
+        ptr = (unsigned char*)mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, map_fd,
+                                   getpagesize());
         ASSERT_TRUE(ptr != NULL);
-
         ASSERT_EQ(ptr[0], 0xaa);
-        ASSERT_EQ(ptr[PAGE_SIZE - 1], 0xaa);
-
-        ASSERT_EQ(0, munmap(ptr, PAGE_SIZE));
-
+        ASSERT_EQ(ptr[getpagesize() - 1], 0xaa);
+        ASSERT_EQ(0, munmap(ptr, getpagesize()));
         ASSERT_EQ(0, close(map_fd));
     }
 }
 
-TEST_F(Map, MapCached)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapCached) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
             unsigned int flags = ION_FLAG_CACHED;
 
-            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), flags, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void *ptr;
+            void* ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
-
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
@@ -135,23 +102,22 @@
     }
 }
 
-TEST_F(Map, MapCachedNeedsSync)
-{
-    static const size_t allocationSizes[] = {4*1024, 64*1024, 1024*1024, 2*1024*1024};
-    for (unsigned int heapMask : m_allHeaps) {
+TEST_F(Map, MapCachedNeedsSync) {
+    static const size_t allocationSizes[] = {4 * 1024, 64 * 1024, 1024 * 1024, 2 * 1024 * 1024};
+    for (const auto& heap : ion_heaps) {
         for (size_t size : allocationSizes) {
-            SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
+            SCOPED_TRACE(::testing::Message()
+                         << "heap:" << heap.name << ":" << heap.type << ":" << heap.heap_id);
             SCOPED_TRACE(::testing::Message() << "size " << size);
             int map_fd = -1;
             unsigned int flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;
 
-            ASSERT_EQ(0, ion_alloc_fd(m_ionFd, size, 0, heapMask, flags, &map_fd));
+            ASSERT_EQ(0, ion_alloc_fd(ionfd, size, 0, (1 << heap.heap_id), flags, &map_fd));
             ASSERT_GE(map_fd, 0);
 
-            void *ptr;
+            void* ptr;
             ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0);
             ASSERT_TRUE(ptr != NULL);
-
             ASSERT_EQ(0, close(map_fd));
 
             memset(ptr, 0xaa, size);
diff --git a/liblog/logger.h b/liblog/logger.h
index 1f632c0..accb6e7 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <stdatomic.h>
-#include <stdbool.h>
 
 #include <cutils/list.h>
 #include <log/log.h>
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 7fa3f43..a4b3cd7 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -407,63 +407,15 @@
 }
 
 int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
-  struct iovec vec[3];
-  char tmp_tag[32];
-
   if (!tag) tag = "";
 
-  /* XXX: This needs to go! */
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wstring-plus-int"
-  if (bufID != LOG_ID_RADIO) {
-    switch (tag[0]) {
-      case 'H':
-        if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
-        goto inform;
-      case 'R':
-        /* Any log tag with "RIL" as the prefix */
-        if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
-        goto inform;
-      case 'Q':
-        /* Any log tag with "QC_RIL" as the prefix */
-        if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
-        goto inform;
-      case 'I':
-        /* Any log tag with "IMS" as the prefix */
-        if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
-        goto inform;
-      case 'A':
-        if (strcmp(tag + 1, "AT" + 1)) break;
-        goto inform;
-      case 'G':
-        if (strcmp(tag + 1, "GSM" + 1)) break;
-        goto inform;
-      case 'S':
-        if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
-        goto inform;
-      case 'C':
-        if (strcmp(tag + 1, "CDMA" + 1)) break;
-        goto inform;
-      case 'P':
-        if (strcmp(tag + 1, "PHONE" + 1)) break;
-      /* FALLTHRU */
-      inform:
-        bufID = LOG_ID_RADIO;
-        snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
-        tag = tmp_tag;
-        [[fallthrough]];
-      default:
-        break;
-    }
-  }
-#pragma clang diagnostic pop
-
 #if __BIONIC__
   if (prio == ANDROID_LOG_FATAL) {
     android_set_abort_message(msg);
   }
 #endif
 
+  struct iovec vec[3];
   vec[0].iov_base = (unsigned char*)&prio;
   vec[0].iov_len = 1;
   vec[1].iov_base = (void*)tag;
diff --git a/liblog/logprint.cpp b/liblog/logprint.cpp
index 6b5ea4c..3a54445 100644
--- a/liblog/logprint.cpp
+++ b/liblog/logprint.cpp
@@ -26,7 +26,6 @@
 #ifndef __MINGW32__
 #include <pwd.h>
 #endif
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index ba27fd7..eaac97f 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -17,7 +17,6 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index e851100..4632b32 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -20,7 +20,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
diff --git a/liblog/stderr_write.cpp b/liblog/stderr_write.cpp
index e324a7c..e76673f 100644
--- a/liblog/stderr_write.cpp
+++ b/liblog/stderr_write.cpp
@@ -27,7 +27,6 @@
  */
 
 #include <errno.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 1f87b3e..d931ed1 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -3004,21 +3004,6 @@
   EXPECT_LE(0, android_log_destroy(&ctx));
   ASSERT_TRUE(NULL == ctx);
 }
-
-TEST(liblog, android_log_write_list_buffer) {
-  __android_log_event_list ctx(1005);
-  ctx << 1005 << "tag_def"
-      << "(tag|1),(name|3),(format|3)";
-  std::string buffer(ctx);
-  ctx.close();
-
-  char msgBuf[1024];
-  memset(msgBuf, 0, sizeof(msgBuf));
-  EXPECT_EQ(android_log_buffer_to_string(buffer.data(), buffer.length(), msgBuf,
-                                         sizeof(msgBuf)),
-            0);
-  EXPECT_STREQ(msgBuf, "[1005,tag_def,(tag|1),(name|3),(format|3)]");
-}
 #endif  // USING_LOGGER_DEFAULT
 
 #ifdef USING_LOGGER_DEFAULT  // Do not retest pmsg functionality
diff --git a/liblog/tests/liblog_test_default.cpp b/liblog/tests/liblog_test_default.cpp
index 9fc443c..2edea27 100644
--- a/liblog/tests/liblog_test_default.cpp
+++ b/liblog/tests/liblog_test_default.cpp
@@ -2,4 +2,5 @@
 #include <log/log_transport.h>
 #define TEST_LOGGER LOGGER_DEFAULT
 #endif
+#define USING_LOGGER_DEFAULT
 #include "liblog_test.cpp"
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
index fc022bd..8dcc77b 100644
--- a/libmeminfo/Android.bp
+++ b/libmeminfo/Android.bp
@@ -26,10 +26,17 @@
         "liblog",
         "libprocinfo",
     ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+
+    },
 }
 
 cc_library {
     name: "libmeminfo",
+    host_supported: true,
     defaults: ["libmeminfo_defaults"],
     export_include_dirs: ["include"],
     export_shared_lib_headers: ["libbase"],
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
index 1fb4151..f782ec5 100644
--- a/libmeminfo/include/meminfo/procmeminfo.h
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -45,6 +45,9 @@
     // vector.
     const std::vector<Vma>& MapsWithPageIdle();
 
+    // Same as Maps() except, do not read the usage stats for each map.
+    const std::vector<Vma>& MapsWithoutUsageStats();
+
     // 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.
     //
@@ -88,7 +91,7 @@
     ~ProcMemInfo() = default;
 
   private:
-    bool ReadMaps(bool get_wss, bool use_pageidle = false);
+    bool ReadMaps(bool get_wss, bool use_pageidle = false, bool get_usage_stats = true);
     bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle);
 
     pid_t pid_;
diff --git a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
index eb53e57..7bba599 100644
--- a/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
+++ b/libmeminfo/libdmabufinfo/dmabufinfo_test.cpp
@@ -17,6 +17,7 @@
 #include <inttypes.h>
 #include <linux/dma-buf.h>
 #include <poll.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -230,7 +231,7 @@
     DmaBufTester() : ion_fd(ion_open()), ion_heap_mask(get_ion_heap_mask()) {}
 
     ~DmaBufTester() {
-        if (is_valid()) {
+        if (ion_fd >= 0) {
             ion_close(ion_fd);
         }
     }
@@ -241,12 +242,16 @@
         int fd;
         int err = ion_alloc_fd(ion_fd, size, 0, ion_heap_mask, 0, &fd);
         if (err < 0) {
-            return unique_fd{err};
+            printf("Failed ion_alloc_fd, return value: %d\n", err);
+            return unique_fd{};
         }
 
         if (!name.empty()) {
-            err = ioctl(fd, DMA_BUF_SET_NAME, name.c_str());
-            if (err < 0) return unique_fd{-errno};
+            if (ioctl(fd, DMA_BUF_SET_NAME, name.c_str()) == -1) {
+                printf("Failed ioctl(DMA_BUF_SET_NAME): %s\n", strerror(errno));
+                close(fd);
+                return unique_fd{};
+            }
         }
 
         return unique_fd{fd};
@@ -306,7 +311,7 @@
         return ret;
     }
 
-    unique_fd ion_fd;
+    int ion_fd;
     const int ion_heap_mask;
 };
 
diff --git a/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
index a16c3fd..a6e7f69 100644
--- a/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
+++ b/libmeminfo/libdmabufinfo/include/dmabufinfo/dmabufinfo.h
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <set>
 #include <string>
 #include <vector>
 #include <unordered_map>
@@ -33,6 +34,7 @@
         : inode_(inode), size_(size), count_(count), exporter_(exporter), name_(name) {
         total_refs_ = 0;
     }
+    DmaBuffer() = default;
     ~DmaBuffer() = default;
 
     // Adds one file descriptor reference for the given pid
@@ -54,11 +56,13 @@
     ino_t inode() const { return inode_; }
     uint64_t total_refs() const { return total_refs_; }
     uint64_t count() const { return count_; };
+    const std::set<pid_t>& pids() const { return pids_; }
     const std::string& name() const { return name_; }
     const std::string& exporter() const { return exporter_; }
     void SetName(const std::string& name) { name_ = name; }
     void SetExporter(const std::string& exporter) { exporter_ = exporter; }
     void SetCount(uint64_t count) { count_ = count; }
+    uint64_t Pss() const { return size_ / pids_.size(); }
 
     bool operator==(const DmaBuffer& rhs) {
         return (inode_ == rhs.inode()) && (size_ == rhs.size()) && (name_ == rhs.name()) &&
@@ -70,6 +74,7 @@
     uint64_t size_;
     uint64_t count_;
     uint64_t total_refs_;
+    std::set<pid_t> pids_;
     std::string exporter_;
     std::string name_;
     std::unordered_map<pid_t, int> fdrefs_;
@@ -80,6 +85,7 @@
         auto [it, inserted] = map->insert(std::make_pair(pid, 1));
         if (!inserted)
             it->second++;
+        pids_.insert(pid);
     }
 };
 
diff --git a/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
index 9a80c82..48901b1 100644
--- a/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
+++ b/libmeminfo/libdmabufinfo/tools/dmabuf_dump.cpp
@@ -16,6 +16,7 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <getopt.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -36,9 +37,10 @@
 
 [[noreturn]] static void usage(int exit_status) {
     fprintf(stderr,
-            "Usage: %s [PID] \n"
-            "\t If PID is supplied, the dmabuf information for this process is shown.\n"
-            "\t Otherwise, shows the information for all processes.\n",
+            "Usage: %s [-ah] [PID] \n"
+            "-a\t show all dma buffers (ion) in big table, [buffer x process] grid \n"
+            "-h\t show this help\n"
+            "  \t If PID is supplied, the dmabuf information for that process is shown.\n",
             getprogname());
 
     exit(exit_status);
@@ -54,11 +56,7 @@
     return line;
 }
 
-static void AddPidsToSet(const std::unordered_map<pid_t, int>& map, std::set<pid_t>* set) {
-    for (auto it = map.begin(); it != map.end(); ++it) set->insert(it->first);
-}
-
-static void PrintDmaBufInfo(const std::vector<DmaBuffer>& bufs) {
+static void PrintDmaBufTable(const std::vector<DmaBuffer>& bufs) {
     if (bufs.empty()) {
         printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
         return;
@@ -66,9 +64,8 @@
 
     // Find all unique pids in the input vector, create a set
     std::set<pid_t> pid_set;
-    for (int i = 0; i < bufs.size(); i++) {
-        AddPidsToSet(bufs[i].fdrefs(), &pid_set);
-        AddPidsToSet(bufs[i].maprefs(), &pid_set);
+    for (auto& buf : bufs) {
+        pid_set.insert(buf.pids().begin(), buf.pids().end());
     }
 
     // Format the header string spaced and separated with '|'
@@ -126,50 +123,144 @@
     return;
 }
 
-int main(int argc, char* argv[]) {
-    pid_t pid = -1;
-    std::vector<DmaBuffer> bufs;
-    bool show_all = true;
+static void PrintDmaBufPerProcess(const std::vector<DmaBuffer>& bufs) {
+    if (bufs.empty()) {
+        printf("dmabuf info not found ¯\\_(ツ)_/¯\n");
+        return;
+    }
 
-    if (argc > 1) {
-        if (sscanf(argv[1], "%d", &pid) == 1) {
-            show_all = false;
-        } else {
+    // Create a reverse map from pid to dmabufs
+    std::unordered_map<pid_t, std::set<ino_t>> pid_to_inodes = {};
+    uint64_t total_size = 0;  // Total size of dmabufs in the system
+    uint64_t kernel_rss = 0;  // Total size of dmabufs NOT mapped or opened by a process
+    for (auto& buf : bufs) {
+        for (auto pid : buf.pids()) {
+            pid_to_inodes[pid].insert(buf.inode());
+        }
+        total_size += buf.size();
+        if (buf.fdrefs().empty() && buf.maprefs().empty()) {
+            kernel_rss += buf.size();
+        }
+    }
+    // Create an inode to dmabuf map. We know inodes are unique..
+    std::unordered_map<ino_t, DmaBuffer> inode_to_dmabuf;
+    for (auto buf : bufs) {
+        inode_to_dmabuf[buf.inode()] = buf;
+    }
+
+    uint64_t total_rss = 0, total_pss = 0;
+    for (auto& [pid, inodes] : pid_to_inodes) {
+        uint64_t pss = 0;
+        uint64_t rss = 0;
+
+        printf("%16s:%-5d\n", GetProcessComm(pid).c_str(), pid);
+        printf("%22s %16s %16s %16s %16s\n", "Name", "Rss", "Pss", "nr_procs", "Inode");
+        for (auto& inode : inodes) {
+            DmaBuffer& buf = inode_to_dmabuf[inode];
+            printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16zu %16" PRIuMAX "\n",
+                   buf.name().empty() ? "<unknown>" : buf.name().c_str(), buf.size() / 1024,
+                   buf.Pss() / 1024, buf.pids().size(), static_cast<uintmax_t>(buf.inode()));
+            rss += buf.size();
+            pss += buf.Pss();
+        }
+        printf("%22s %13" PRIu64 " kB %13" PRIu64 " kB %16s\n", "PROCESS TOTAL", rss / 1024,
+               pss / 1024, "");
+        printf("----------------------\n");
+        total_rss += rss;
+        total_pss += pss;
+    }
+    printf("dmabuf total: %" PRIu64 " kB kernel_rss: %" PRIu64 " kB userspace_rss: %" PRIu64
+           " kB userspace_pss: %" PRIu64 " kB\n ",
+           total_size / 1024, kernel_rss / 1024, total_rss / 1024, total_pss / 1024);
+}
+
+static bool ReadDmaBufs(std::vector<DmaBuffer>* bufs) {
+    bufs->clear();
+
+    if (!ReadDmaBufInfo(bufs)) {
+        fprintf(stderr, "debugfs entry for dmabuf not available, skipping\n");
+        return false;
+    }
+
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
+    if (!dir) {
+        fprintf(stderr, "Failed to open /proc directory\n");
+        bufs->clear();
+        return false;
+    }
+
+    struct dirent* dent;
+    while ((dent = readdir(dir.get()))) {
+        if (dent->d_type != DT_DIR) continue;
+
+        int pid = atoi(dent->d_name);
+        if (pid == 0) {
+            continue;
+        }
+
+        if (!AppendDmaBufInfo(pid, bufs)) {
+            fprintf(stderr, "Unable to read dmabuf info for pid %d\n", pid);
+            bufs->clear();
+            return false;
+        }
+    }
+
+    return true;
+}
+
+int main(int argc, char* argv[]) {
+    struct option longopts[] = {{"all", no_argument, nullptr, 'a'},
+                                {"help", no_argument, nullptr, 'h'},
+                                {0, 0, nullptr, 0}};
+
+    int opt;
+    bool show_table = false;
+    while ((opt = getopt_long(argc, argv, "ah", longopts, nullptr)) != -1) {
+        switch (opt) {
+            case 'a':
+                show_table = true;
+                break;
+            case 'h':
+                usage(EXIT_SUCCESS);
+            default:
+                usage(EXIT_FAILURE);
+        }
+    }
+
+    pid_t pid = -1;
+    if (optind < argc) {
+        if (show_table) {
+            fprintf(stderr, "Invalid arguments: -a does not need arguments\n");
+            usage(EXIT_FAILURE);
+        }
+        if (optind != (argc - 1)) {
+            fprintf(stderr, "Invalid arguments - only one [PID] argument is allowed\n");
+            usage(EXIT_FAILURE);
+        }
+        pid = atoi(argv[optind]);
+        if (pid == 0) {
+            fprintf(stderr, "Invalid process id %s\n", argv[optind]);
             usage(EXIT_FAILURE);
         }
     }
 
-    if (show_all) {
-        if (!ReadDmaBufInfo(&bufs)) {
-            std::cerr << "debugfs entry for dmabuf not available, skipping" << std::endl;
-            bufs.clear();
-        }
-        std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir);
-        if (!dir) {
-            std::cerr << "Failed to open /proc directory" << std::endl;
+    std::vector<DmaBuffer> bufs;
+    if (pid != -1) {
+        if (!ReadDmaBufInfo(pid, &bufs)) {
+            fprintf(stderr, "Unable to read dmabuf info for %d\n", pid);
             exit(EXIT_FAILURE);
         }
-        struct dirent* dent;
-        while ((dent = readdir(dir.get()))) {
-            if (dent->d_type != DT_DIR) continue;
-
-            int matched = sscanf(dent->d_name, "%d", &pid);
-            if (matched != 1) {
-                continue;
-            }
-
-            if (!AppendDmaBufInfo(pid, &bufs)) {
-                std::cerr << "Unable to read dmabuf info for pid " << pid << std::endl;
-                exit(EXIT_FAILURE);
-            }
-        }
     } else {
-        if (!ReadDmaBufInfo(pid, &bufs)) {
-            std::cerr << "Unable to read dmabuf info" << std::endl;
-            exit(EXIT_FAILURE);
-        }
+        if (!ReadDmaBufs(&bufs)) exit(EXIT_FAILURE);
     }
 
-    PrintDmaBufInfo(bufs);
+    // Show the old dmabuf table, inode x process
+    if (show_table) {
+        PrintDmaBufTable(bufs);
+        return 0;
+    }
+
+    PrintDmaBufPerProcess(bufs);
+
     return 0;
 }
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 5451ca3..4c2be91 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -60,6 +61,103 @@
     EXPECT_FALSE(maps.empty());
 }
 
+TEST(ProcMemInfo, MapsUsageNotEmpty) {
+    ProcMemInfo proc_mem(pid);
+    const std::vector<Vma>& maps = proc_mem.Maps();
+    EXPECT_FALSE(maps.empty());
+    uint64_t total_pss = 0;
+    uint64_t total_rss = 0;
+    uint64_t total_uss = 0;
+    for (auto& map : maps) {
+        ASSERT_NE(0, map.usage.vss);
+        total_rss += map.usage.rss;
+        total_pss += map.usage.pss;
+        total_uss += map.usage.uss;
+    }
+
+    // Crude check that stats are actually being read.
+    EXPECT_NE(0, total_rss) << "RSS zero for all maps, that is not possible.";
+    EXPECT_NE(0, total_pss) << "PSS zero for all maps, that is not possible.";
+    EXPECT_NE(0, total_uss) << "USS zero for all maps, that is not possible.";
+}
+
+TEST(ProcMemInfo, MapsUsageEmpty) {
+    ProcMemInfo proc_mem(pid);
+    const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+    EXPECT_FALSE(maps.empty());
+    // Verify that all usage stats are zero in every map.
+    for (auto& map : maps) {
+        ASSERT_EQ(0, map.usage.vss);
+        ASSERT_EQ(0, map.usage.rss);
+        ASSERT_EQ(0, map.usage.pss);
+        ASSERT_EQ(0, map.usage.uss);
+        ASSERT_EQ(0, map.usage.swap);
+        ASSERT_EQ(0, map.usage.swap_pss);
+        ASSERT_EQ(0, map.usage.private_clean);
+        ASSERT_EQ(0, map.usage.private_dirty);
+        ASSERT_EQ(0, map.usage.shared_clean);
+        ASSERT_EQ(0, map.usage.shared_dirty);
+    }
+}
+
+TEST(ProcMemInfo, PageMapPresent) {
+    static constexpr size_t kNumPages = 20;
+    size_t pagesize = getpagesize();
+    void* ptr = mmap(nullptr, pagesize * (kNumPages + 2), PROT_READ | PROT_WRITE,
+                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+    ASSERT_NE(MAP_FAILED, ptr);
+
+    // Unmap the first page and the last page so that we guarantee this
+    // map is in a map by itself.
+    ASSERT_EQ(0, munmap(ptr, pagesize));
+    uintptr_t addr = reinterpret_cast<uintptr_t>(ptr) + pagesize;
+    ASSERT_EQ(0, munmap(reinterpret_cast<void*>(addr + kNumPages * pagesize), pagesize));
+
+    ProcMemInfo proc_mem(getpid());
+    const std::vector<Vma>& maps = proc_mem.MapsWithoutUsageStats();
+    ASSERT_FALSE(maps.empty());
+
+    // Find the vma associated with our previously created map.
+    const Vma* test_vma = nullptr;
+    for (const Vma& vma : maps) {
+        if (vma.start == addr) {
+            test_vma = &vma;
+            break;
+        }
+    }
+    ASSERT_TRUE(test_vma != nullptr) << "Cannot find test map.";
+
+    // Verify that none of the pages are listed as present.
+    std::vector<uint64_t> pagemap;
+    ASSERT_TRUE(proc_mem.PageMap(*test_vma, &pagemap));
+    ASSERT_EQ(kNumPages, pagemap.size());
+    for (size_t i = 0; i < pagemap.size(); i++) {
+        EXPECT_FALSE(android::meminfo::page_present(pagemap[i]))
+                << "Page " << i << " is present and it should not be.";
+    }
+
+    // Make some of the pages present and verify that we see them
+    // as present.
+    uint8_t* data = reinterpret_cast<uint8_t*>(addr);
+    data[0] = 1;
+    data[pagesize * 5] = 1;
+    data[pagesize * 11] = 1;
+
+    ASSERT_TRUE(proc_mem.PageMap(*test_vma, &pagemap));
+    ASSERT_EQ(kNumPages, pagemap.size());
+    for (size_t i = 0; i < pagemap.size(); i++) {
+        if (i == 0 || i == 5 || i == 11) {
+            EXPECT_TRUE(android::meminfo::page_present(pagemap[i]))
+                    << "Page " << i << " is not present and it should be.";
+        } else {
+            EXPECT_FALSE(android::meminfo::page_present(pagemap[i]))
+                    << "Page " << i << " is present and it should not be.";
+        }
+    }
+
+    ASSERT_EQ(0, munmap(reinterpret_cast<void*>(addr), kNumPages * pagesize));
+}
+
 TEST(ProcMemInfo, WssEmpty) {
     // If we created the object for getting usage,
     // the working set must be empty
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
index caf6e86..6f68ab4 100644
--- a/libmeminfo/procmeminfo.cpp
+++ b/libmeminfo/procmeminfo.cpp
@@ -130,6 +130,14 @@
     return maps_;
 }
 
+const std::vector<Vma>& ProcMemInfo::MapsWithoutUsageStats() {
+    if (maps_.empty() && !ReadMaps(get_wss_, false, false)) {
+        LOG(ERROR) << "Failed to read maps for Process " << pid_;
+    }
+
+    return maps_;
+}
+
 const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
     if (!maps_.empty()) {
         return maps_;
@@ -213,29 +221,30 @@
     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) {
+    if (pagemap_fd == -1) {
         PLOG(ERROR) << "Failed to open " << pagemap_file;
         return false;
     }
 
     uint64_t nr_pages = (vma.end - vma.start) / getpagesize();
-    pagemap->reserve(nr_pages);
+    pagemap->resize(nr_pages);
 
-    uint64_t idx = vma.start / getpagesize();
-    uint64_t last = idx + nr_pages;
-    uint64_t val;
-    for (; idx < last; idx++) {
-        if (pread64(pagemap_fd, &val, sizeof(uint64_t), idx * sizeof(uint64_t)) < 0) {
-            PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
-            return false;
-        }
-        pagemap->emplace_back(val);
+    size_t bytes_to_read = sizeof(uint64_t) * nr_pages;
+    off64_t start_addr = (vma.start / getpagesize()) * sizeof(uint64_t);
+    ssize_t bytes_read = pread64(pagemap_fd, pagemap->data(), bytes_to_read, start_addr);
+    if (bytes_read == -1) {
+        PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+        return false;
+    } else if (static_cast<size_t>(bytes_read) != bytes_to_read) {
+        LOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_
+                   << ": read bytes " << bytes_read << " expected bytes " << bytes_to_read;
+        return false;
     }
 
     return true;
 }
 
-bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle) {
+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
     // unnecessarily retain and update this object in memory (which can get significantly large).
@@ -256,6 +265,10 @@
         return false;
     }
 
+    if (!get_usage_stats) {
+        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)));
@@ -312,12 +325,12 @@
                                     cur_page * sizeof(uint64_t));
             if (bytes != total_bytes) {
                 if (bytes == -1) {
-                    PLOG(ERROR) << "Failed to read page data at offset "
+                    PLOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
                                 << cur_page * sizeof(uint64_t);
                 } else {
-                    LOG(ERROR) << "Failed to read page data at offset "
-                               << cur_page * sizeof(uint64_t) << " read bytes " << sizeof(uint64_t)
-                               << " expected bytes " << bytes;
+                    LOG(ERROR) << "Failed to read page data at offset 0x" << std::hex
+                               << cur_page * sizeof(uint64_t) << std::dec << " read bytes " << bytes
+                               << " expected bytes " << total_bytes;
                 }
                 return false;
             }
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
index 2e89c41..3968c09 100644
--- a/libmeminfo/tools/Android.bp
+++ b/libmeminfo/tools/Android.bp
@@ -56,6 +56,7 @@
 
 cc_binary {
     name: "showmap",
+    host_supported: true,
     cflags: [
         "-Wall",
         "-Werror",
@@ -66,6 +67,12 @@
         "libbase",
         "libmeminfo",
     ],
+
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
 
 cc_binary {
diff --git a/libmeminfo/tools/showmap.cpp b/libmeminfo/tools/showmap.cpp
index a80fa76..8ea2108 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/signal.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -56,7 +57,7 @@
 static VmaInfo g_total;
 static std::vector<VmaInfo> g_vmas;
 
-[[noreturn]] static void usage(int exit_status) {
+[[noreturn]] static void usage(const char* progname, int exit_status) {
     fprintf(stderr,
             "%s [-aqtv] [-f FILE] PID\n"
             "-a\taddresses (show virtual memory map)\n"
@@ -64,7 +65,7 @@
             "-t\tterse (show only items with private pages)\n"
             "-v\tverbose (don't coalesce maps with the same name)\n"
             "-f\tFILE (read from input from FILE instead of PID)\n",
-            getprogname());
+            progname);
 
     exit(exit_status);
 }
@@ -239,22 +240,22 @@
                 g_filename = optarg;
                 break;
             case 'h':
-                usage(EXIT_SUCCESS);
+                usage(argv[0], EXIT_SUCCESS);
             default:
-                usage(EXIT_FAILURE);
+                usage(argv[0], EXIT_FAILURE);
         }
     }
 
     if (g_filename.empty()) {
         if ((argc - 1) < optind) {
             fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
-            usage(EXIT_FAILURE);
+            usage(argv[0], EXIT_FAILURE);
         }
 
         g_pid = atoi(argv[optind]);
         if (g_pid <= 0) {
             fprintf(stderr, "Invalid process id %s\n", argv[optind]);
-            usage(EXIT_FAILURE);
+            usage(argv[0], EXIT_FAILURE);
         }
 
         g_filename = ::android::base::StringPrintf("/proc/%d/smaps", g_pid);
diff --git a/libmeminfo/vts/AndroidTest.xml b/libmeminfo/vts/AndroidTest.xml
index 530d16e..9614025 100644
--- a/libmeminfo/vts/AndroidTest.xml
+++ b/libmeminfo/vts/AndroidTest.xml
@@ -24,6 +24,7 @@
         <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_meminfo_test/vts_meminfo_test" />
         <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_meminfo_test/vts_meminfo_test" />
         <option name="binary-test-type" value="gtest"/>
+        <option name="precondition-first-api-level" value="29" />
         <option name="test-timeout" value="10m"/>
     </test>
 </configuration>
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
index a2824d1..78da46c 100644
--- a/libmodprobe/Android.bp
+++ b/libmodprobe/Android.bp
@@ -3,6 +3,7 @@
     cflags: [
         "-Werror",
     ],
+    vendor_available: true,
     recovery_available: true,
     srcs: [
         "libmodprobe.cpp",
diff --git a/libmodprobe/OWNERS b/libmodprobe/OWNERS
new file mode 100644
index 0000000..4b770b1
--- /dev/null
+++ b/libmodprobe/OWNERS
@@ -0,0 +1,2 @@
+tomcherry@google.com
+smuckle@google.com
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 0ec766a..dcb4ffb 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <set>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -25,12 +26,21 @@
     Modprobe(const std::vector<std::string>&);
 
     bool LoadListedModules();
-    bool LoadWithAliases(const std::string& module_name, bool strict);
+    bool LoadWithAliases(const std::string& module_name, bool strict,
+                         const std::string& parameters = "");
+    bool Remove(const std::string& module_name);
+    std::vector<std::string> ListModules(const std::string& pattern);
+    bool GetAllDependencies(const std::string& module, std::vector<std::string>* pre_dependencies,
+                            std::vector<std::string>* dependencies,
+                            std::vector<std::string>* post_dependencies);
+    void EnableBlacklist(bool enable);
+    void EnableVerbose(bool enable);
 
   private:
     std::string MakeCanonical(const std::string& module_path);
-    bool InsmodWithDeps(const std::string& module_name);
-    bool Insmod(const std::string& path_name);
+    bool InsmodWithDeps(const std::string& module_name, const std::string& parameters);
+    bool Insmod(const std::string& path_name, const std::string& parameters);
+    bool Rmmod(const std::string& module_name);
     std::vector<std::string> GetDependencies(const std::string& module);
     bool ModuleExists(const std::string& module_name);
 
@@ -39,6 +49,7 @@
     bool ParseSoftdepCallback(const std::vector<std::string>& args);
     bool ParseLoadCallback(const std::vector<std::string>& args);
     bool ParseOptionsCallback(const std::vector<std::string>& args);
+    bool ParseBlacklistCallback(const std::vector<std::string>& args);
     void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);
 
     std::vector<std::pair<std::string, std::string>> module_aliases_;
@@ -47,4 +58,6 @@
     std::vector<std::pair<std::string, std::string>> module_post_softdep_;
     std::vector<std::string> module_load_;
     std::unordered_map<std::string, std::string> module_options_;
+    std::set<std::string> module_blacklist_;
+    bool blacklist_enabled = false;
 };
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 01cf2e3..73ae15b 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -194,6 +194,31 @@
     return true;
 }
 
+bool Modprobe::ParseBlacklistCallback(const std::vector<std::string>& args) {
+    auto it = args.begin();
+    const std::string& type = *it++;
+
+    if (type != "blacklist") {
+        LOG(ERROR) << "non-blacklist line encountered in modules.blacklist";
+        return false;
+    }
+
+    if (args.size() != 2) {
+        LOG(ERROR) << "lines in modules.blacklist must have exactly 2 entries, not " << args.size();
+        return false;
+    }
+
+    const std::string& module = *it++;
+
+    const std::string& canonical_name = MakeCanonical(module);
+    if (canonical_name.empty()) {
+        return false;
+    }
+    this->module_blacklist_.emplace(canonical_name);
+
+    return true;
+}
+
 void Modprobe::ParseCfg(const std::string& cfg,
                         std::function<bool(const std::vector<std::string>&)> f) {
     std::string cfg_contents;
@@ -231,6 +256,23 @@
 
         auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);
         ParseCfg(base_path + "/modules.options", options_callback);
+
+        auto blacklist_callback = std::bind(&Modprobe::ParseBlacklistCallback, this, _1);
+        ParseCfg(base_path + "/modules.blacklist", blacklist_callback);
+    }
+
+    android::base::SetMinimumLogSeverity(android::base::INFO);
+}
+
+void Modprobe::EnableBlacklist(bool enable) {
+    blacklist_enabled = enable;
+}
+
+void Modprobe::EnableVerbose(bool enable) {
+    if (enable) {
+        android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+    } else {
+        android::base::SetMinimumLogSeverity(android::base::INFO);
     }
 }
 
@@ -242,7 +284,7 @@
     return it->second;
 }
 
-bool Modprobe::InsmodWithDeps(const std::string& module_name) {
+bool Modprobe::InsmodWithDeps(const std::string& module_name, const std::string& parameters) {
     if (module_name.empty()) {
         LOG(ERROR) << "Need valid module name, given: " << module_name;
         return false;
@@ -256,11 +298,8 @@
 
     // load module dependencies in reverse order
     for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {
-        const std::string& canonical_name = MakeCanonical(*dep);
-        if (canonical_name.empty()) {
-            return false;
-        }
-        if (!LoadWithAliases(canonical_name, true)) {
+        LOG(VERBOSE) << "Loading hard dep for '" << module_name << "': " << *dep;
+        if (!LoadWithAliases(*dep, true)) {
             return false;
         }
     }
@@ -268,18 +307,20 @@
     // try to load soft pre-dependencies
     for (const auto& [module, softdep] : module_pre_softdep_) {
         if (module_name == module) {
+            LOG(VERBOSE) << "Loading soft pre-dep for '" << module << "': " << softdep;
             LoadWithAliases(softdep, false);
         }
     }
 
     // load target module itself with args
-    if (!Insmod(dependencies[0])) {
+    if (!Insmod(dependencies[0], parameters)) {
         return false;
     }
 
     // try to load soft post-dependencies
     for (const auto& [module, softdep] : module_post_softdep_) {
         if (module_name == module) {
+            LOG(VERBOSE) << "Loading soft post-dep for '" << module << "': " << softdep;
             LoadWithAliases(softdep, false);
         }
     }
@@ -287,25 +328,27 @@
     return true;
 }
 
-bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict) {
-    std::set<std::string> modules_to_load = {module_name};
+bool Modprobe::LoadWithAliases(const std::string& module_name, bool strict,
+                               const std::string& parameters) {
+    std::set<std::string> modules_to_load = {MakeCanonical(module_name)};
     bool module_loaded = false;
 
     // use aliases to expand list of modules to load (multiple modules
     // may alias themselves to the requested name)
     for (const auto& [alias, aliased_module] : module_aliases_) {
         if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue;
+        LOG(VERBOSE) << "Found alias for '" << module_name << "': '" << aliased_module;
         modules_to_load.emplace(aliased_module);
     }
 
     // attempt to load all modules aliased to this name
     for (const auto& module : modules_to_load) {
         if (!ModuleExists(module)) continue;
-        if (InsmodWithDeps(module)) module_loaded = true;
+        if (InsmodWithDeps(module, parameters)) module_loaded = true;
     }
 
     if (strict && !module_loaded) {
-        LOG(ERROR) << "LoadWithAliases did not find a module for " << module_name;
+        LOG(ERROR) << "LoadWithAliases was unable to load " << module_name;
         return false;
     }
     return true;
@@ -319,3 +362,64 @@
     }
     return true;
 }
+
+bool Modprobe::Remove(const std::string& module_name) {
+    auto dependencies = GetDependencies(MakeCanonical(module_name));
+    if (dependencies.empty()) {
+        LOG(ERROR) << "Empty dependencies for module " << module_name;
+        return false;
+    }
+    if (!Rmmod(dependencies[0])) {
+        return false;
+    }
+    for (auto dep = dependencies.begin() + 1; dep != dependencies.end(); ++dep) {
+        Rmmod(*dep);
+    }
+    return true;
+}
+
+std::vector<std::string> Modprobe::ListModules(const std::string& pattern) {
+    std::vector<std::string> rv;
+    for (const auto& [module, deps] : module_deps_) {
+        // Attempt to match both the canonical module name and the module filename.
+        if (!fnmatch(pattern.c_str(), module.c_str(), 0)) {
+            rv.emplace_back(module);
+        } else if (!fnmatch(pattern.c_str(), basename(deps[0].c_str()), 0)) {
+            rv.emplace_back(deps[0]);
+        }
+    }
+    return rv;
+}
+
+bool Modprobe::GetAllDependencies(const std::string& module,
+                                  std::vector<std::string>* pre_dependencies,
+                                  std::vector<std::string>* dependencies,
+                                  std::vector<std::string>* post_dependencies) {
+    std::string canonical_name = MakeCanonical(module);
+    if (pre_dependencies) {
+        pre_dependencies->clear();
+        for (const auto& [it_module, it_softdep] : module_pre_softdep_) {
+            if (canonical_name == it_module) {
+                pre_dependencies->emplace_back(it_softdep);
+            }
+        }
+    }
+    if (dependencies) {
+        dependencies->clear();
+        auto hard_deps = GetDependencies(canonical_name);
+        if (hard_deps.empty()) {
+            return false;
+        }
+        for (auto dep = hard_deps.rbegin(); dep != hard_deps.rend(); dep++) {
+            dependencies->emplace_back(*dep);
+        }
+    }
+    if (post_dependencies) {
+        for (const auto& [it_module, it_softdep] : module_post_softdep_) {
+            if (canonical_name == it_module) {
+                post_dependencies->emplace_back(it_softdep);
+            }
+        }
+    }
+    return true;
+}
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 5f3a04d..2efcac2 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -22,7 +22,7 @@
 
 #include <modprobe/modprobe.h>
 
-bool Modprobe::Insmod(const std::string& path_name) {
+bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
     android::base::unique_fd fd(
             TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
     if (fd == -1) {
@@ -35,6 +35,9 @@
     if (options_iter != module_options_.end()) {
         options = options_iter->second;
     }
+    if (!parameters.empty()) {
+        options = options + " " + parameters;
+    }
 
     LOG(INFO) << "Loading module " << path_name << " with args \"" << options << "\"";
     int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);
@@ -51,17 +54,32 @@
     return true;
 }
 
+bool Modprobe::Rmmod(const std::string& module_name) {
+    int ret = syscall(__NR_delete_module, MakeCanonical(module_name).c_str(), O_NONBLOCK);
+    if (ret != 0) {
+        PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
+        return false;
+    }
+    return true;
+}
+
 bool Modprobe::ModuleExists(const std::string& module_name) {
     struct stat fileStat;
+    if (blacklist_enabled && module_blacklist_.count(module_name)) {
+        LOG(INFO) << "module " << module_name << " is blacklisted";
+        return false;
+    }
     auto deps = GetDependencies(module_name);
     if (deps.empty()) {
         // missing deps can happen in the case of an alias
         return false;
     }
     if (stat(deps.front().c_str(), &fileStat)) {
+        LOG(INFO) << "module " << module_name << " does not exist";
         return false;
     }
     if (!S_ISREG(fileStat.st_mode)) {
+        LOG(INFO) << "module " << module_name << " is not a regular file";
         return false;
     }
     return true;
diff --git a/libmodprobe/libmodprobe_ext_test.cpp b/libmodprobe/libmodprobe_ext_test.cpp
index 0f073cb..7d817b1 100644
--- a/libmodprobe/libmodprobe_ext_test.cpp
+++ b/libmodprobe/libmodprobe_ext_test.cpp
@@ -29,7 +29,7 @@
 
 #include "libmodprobe_test.h"
 
-bool Modprobe::Insmod(const std::string& path_name) {
+bool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {
     auto deps = GetDependencies(MakeCanonical(path_name));
     if (deps.empty()) {
         return false;
@@ -47,12 +47,29 @@
     if (options_iter != module_options_.end()) {
         options = " " + options_iter->second;
     }
+    if (!parameters.empty()) {
+        options = options + " " + parameters;
+    }
+
     modules_loaded.emplace_back(path_name + options);
     return true;
 }
 
+bool Modprobe::Rmmod(const std::string& module_name) {
+    for (auto it = modules_loaded.begin(); it != modules_loaded.end(); it++) {
+        if (*it == module_name) {
+            modules_loaded.erase(it);
+            return true;
+        }
+    }
+    return false;
+}
+
 bool Modprobe::ModuleExists(const std::string& module_name) {
     auto deps = GetDependencies(module_name);
+    if (blacklist_enabled && module_blacklist_.count(module_name)) {
+        return false;
+    }
     if (deps.empty()) {
         // missing deps can happen in the case of an alias
         return false;
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index 481658d..a711631 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -56,6 +56,14 @@
             "/test13.ko",
     };
 
+    std::vector<std::string> expected_after_remove = {
+            "/test14.ko", "/test15.ko",         "/test1.ko",
+            "/test6.ko",  "/test2.ko",          "/test5.ko",
+            "/test8.ko",  "/test7.ko param1=4", "/test9.ko param_x=1 param_y=2 param_z=3",
+            "/test10.ko", "/test12.ko",         "/test11.ko",
+            "/test13.ko",
+    };
+
     const std::string modules_dep =
             "test1.ko:\n"
             "test2.ko:\n"
@@ -91,6 +99,10 @@
             "options test9.ko param_x=1 param_y=2 param_z=3\n"
             "options test100.ko param_1=1\n";
 
+    const std::string modules_blacklist =
+            "blacklist test9.ko\n"
+            "blacklist test3.ko\n";
+
     const std::string modules_load =
             "test4.ko\n"
             "test1.ko\n"
@@ -101,17 +113,20 @@
             "test11.ko\n";
 
     TemporaryDir dir;
-    ASSERT_TRUE(android::base::WriteStringToFile(
-            modules_alias, std::string(dir.path) + "/modules.alias", 0600, getuid(), getgid()));
+    auto dir_path = std::string(dir.path);
+    ASSERT_TRUE(android::base::WriteStringToFile(modules_alias, dir_path + "/modules.alias", 0600,
+                                                 getuid(), getgid()));
 
-    ASSERT_TRUE(android::base::WriteStringToFile(
-            modules_dep, std::string(dir.path) + "/modules.dep", 0600, getuid(), getgid()));
-    ASSERT_TRUE(android::base::WriteStringToFile(
-            modules_softdep, std::string(dir.path) + "/modules.softdep", 0600, getuid(), getgid()));
-    ASSERT_TRUE(android::base::WriteStringToFile(
-            modules_options, std::string(dir.path) + "/modules.options", 0600, getuid(), getgid()));
-    ASSERT_TRUE(android::base::WriteStringToFile(
-            modules_load, std::string(dir.path) + "/modules.load", 0600, getuid(), getgid()));
+    ASSERT_TRUE(android::base::WriteStringToFile(modules_dep, dir_path + "/modules.dep", 0600,
+                                                 getuid(), getgid()));
+    ASSERT_TRUE(android::base::WriteStringToFile(modules_softdep, dir_path + "/modules.softdep",
+                                                 0600, getuid(), getgid()));
+    ASSERT_TRUE(android::base::WriteStringToFile(modules_options, dir_path + "/modules.options",
+                                                 0600, getuid(), getgid()));
+    ASSERT_TRUE(android::base::WriteStringToFile(modules_load, dir_path + "/modules.load", 0600,
+                                                 getuid(), getgid()));
+    ASSERT_TRUE(android::base::WriteStringToFile(modules_blacklist, dir_path + "/modules.blacklist",
+                                                 0600, getuid(), getgid()));
 
     for (auto i = test_modules.begin(); i != test_modules.end(); ++i) {
         *i = dir.path + *i;
@@ -131,4 +146,21 @@
     }
 
     EXPECT_TRUE(modules_loaded == expected_modules_loaded);
+
+    EXPECT_TRUE(m.Remove("test4"));
+
+    GTEST_LOG_(INFO) << "Expected modules loaded after removing test4 (in order):";
+    for (auto i = expected_after_remove.begin(); i != expected_after_remove.end(); ++i) {
+        *i = dir.path + *i;
+        GTEST_LOG_(INFO) << "\"" << *i << "\"";
+    }
+    GTEST_LOG_(INFO) << "Actual modules loaded after removing test4 (in order):";
+    for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {
+        GTEST_LOG_(INFO) << "\"" << *i << "\"";
+    }
+
+    EXPECT_TRUE(modules_loaded == expected_after_remove);
+
+    m.EnableBlacklist(true);
+    EXPECT_FALSE(m.LoadWithAliases("test4", true));
 }
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index debc43f..b860db9 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -70,3 +70,27 @@
     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/TEST_MAPPING b/libnativeloader/TEST_MAPPING
new file mode 100644
index 0000000..7becb77
--- /dev/null
+++ b/libnativeloader/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "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
index 2d6ce85..8937636 100644
--- a/libnativeloader/include/nativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -22,18 +22,6 @@
 
 __BEGIN_DECLS
 
-/*
- * Initializes anonymous namespaces. The shared_libs_sonames is the list of sonames
- * to be shared by default namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
- *
- * The library_search_path is the search path for anonymous namespace. The anonymous namespace
- * is used in the case when linker cannot identify the caller of dlopen/dlsym. This happens
- * for the code not loaded by dynamic linker; for example calls from the mono-compiled code.
- */
-extern bool android_init_anonymous_namespace(const char* shared_libs_sonames,
-                                             const char* library_search_path);
-
-
 enum {
   /* A regular namespace is the namespace with a custom search path that does
    * not impose any restrictions on the location of native libraries.
@@ -62,8 +50,18 @@
    */
   ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED = 0x08000000,
 
-  ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED |
-                                           ANDROID_NAMESPACE_TYPE_ISOLATED,
+  /* 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,
 };
 
 /*
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index f7f972f..9a33b55 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -33,6 +33,8 @@
 #include "public_libraries.h"
 #include "utils.h"
 
+using android::base::Error;
+
 namespace android::nativeloader {
 
 namespace {
@@ -43,6 +45,7 @@
 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
@@ -121,17 +124,17 @@
   // we might as well end up loading them from /system/lib or /product/lib
   // For now we rely on CTS test to catch things like this but
   // it should probably be addressed in the future.
-  for (const auto& soname : android::base::Split(default_public_libraries(), ":")) {
+  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());
   }
 }
 
-NativeLoaderNamespace* LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
-                                                 jobject class_loader, bool is_shared,
-                                                 jstring dex_path, jstring java_library_path,
-                                                 jstring java_permitted_path,
-                                                 std::string* error_msg) {
+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) {
@@ -156,15 +159,8 @@
     }
   }
 
-  // Initialize the anonymous namespace with the first non-empty library path.
-  if (!library_path.empty() && !initialized_ &&
-      !InitPublicNamespace(library_path.c_str(), error_msg)) {
-    return nullptr;
-  }
-
-  bool found = FindNamespaceByClassLoader(env, class_loader);
-
-  LOG_ALWAYS_FATAL_IF(found, "There is already a namespace associated with this classloader");
+  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;
@@ -212,53 +208,81 @@
 
   // Create the app namespace
   NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
-  auto app_ns =
-      NativeLoaderNamespace::Create(namespace_name, library_path, permitted_path, parent_ns,
-                                    is_shared, target_sdk_version < 24 /* is_greylist_enabled */);
-  if (app_ns.IsNil()) {
-    *error_msg = app_ns.GetError();
-    return nullptr;
+  // 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();
+  bool is_bridged = app_ns->IsBridged();
 
   auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged);
-  if (!app_ns.Link(platform_ns, system_exposed_libraries)) {
-    *error_msg = app_ns.GetError();
-    return nullptr;
+  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.IsNil()) {
-    if (!app_ns.Link(runtime_ns, runtime_public_libraries())) {
-      *error_msg = app_ns.GetError();
-      return nullptr;
+  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.IsNil() && !app_ns.Link(vndk_ns, vndksp_libraries())) {
-      *error_msg = app_ns.GetError();
-      return nullptr;
+    if (vndk_ns) {
+      linked = app_ns->Link(*vndk_ns, vndksp_libraries());
+      if (!linked) {
+        return linked.error();
+      }
     }
   }
 
-  // Note that when vendor_ns is not configured, vendor_ns.IsNil() will be true
-  // and it will result in linking to the default namespace which is expected
-  // behavior in this case.
   if (!vendor_public_libraries().empty()) {
     auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
-    if (!app_ns.Link(vendor_ns, vendor_public_libraries())) {
-      *error_msg = dlerror();
-      return nullptr;
+    // 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));
+  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);
 }
@@ -276,33 +300,6 @@
   return nullptr;
 }
 
-bool LibraryNamespaces::InitPublicNamespace(const char* library_path, std::string* error_msg) {
-  // Ask native bride if this apps library path should be handled by it
-  bool is_native_bridge = NativeBridgeIsPathSupported(library_path);
-
-  // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
-  // code is one example) unknown to linker in which  case linker uses anonymous
-  // namespace. The second argument specifies the search path for the anonymous
-  // namespace which is the library_path of the classloader.
-  initialized_ = android_init_anonymous_namespace(default_public_libraries().c_str(),
-                                                  is_native_bridge ? nullptr : library_path);
-  if (!initialized_) {
-    *error_msg = dlerror();
-    return false;
-  }
-
-  // and now initialize native bridge namespaces if necessary.
-  if (NativeBridgeInitialized()) {
-    initialized_ = NativeBridgeInitAnonymousNamespace(default_public_libraries().c_str(),
-                                                      is_native_bridge ? library_path : nullptr);
-    if (!initialized_) {
-      *error_msg = NativeBridgeGetError();
-    }
-  }
-
-  return initialized_;
-}
-
 NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env,
                                                                            jobject class_loader) {
   jobject parent_class_loader = GetParentClassLoader(env, class_loader);
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
index fd46cdc..7b3efff 100644
--- a/libnativeloader/library_namespaces.h
+++ b/libnativeloader/library_namespaces.h
@@ -25,34 +25,42 @@
 #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) {}
+  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(); }
-  NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader,
-                                bool is_shared, jstring dex_path, jstring java_library_path,
-                                jstring java_permitted_path, std::string* error_msg);
+  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:
-  bool InitPublicNamespace(const char* library_path, std::string* error_msg);
+  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_;
 };
 
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 0c29324..6d3c057 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -92,12 +92,10 @@
                                    jstring permitted_path) {
 #if defined(__ANDROID__)
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-
-  std::string error_msg;
-  bool success = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path,
-                                      library_path, permitted_path, &error_msg) != nullptr;
-  if (!success) {
-    return env->NewStringUTF(error_msg.c_str());
+  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);
@@ -139,11 +137,14 @@
   if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
     // This is the case where the classloader was not created by ApplicationLoaders
     // In this case we create an isolated not-shared namespace for it.
-    std::string create_error_msg;
-    if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */,
-                                   nullptr, library_path, nullptr, &create_error_msg)) == nullptr) {
-      *error_msg = strdup(create_error_msg.c_str());
+    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;
     }
   }
 
@@ -222,12 +223,14 @@
 #if defined(__ANDROID__)
 void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
                                    bool* needs_native_bridge, char** error_msg) {
-  void* handle = ns->Load(path);
-  if (handle == nullptr) {
-    *error_msg = ns->GetError();
+  auto handle = ns->Load(path);
+  if (!handle && error_msg != nullptr) {
+    *error_msg = strdup(handle.error().message().c_str());
   }
-  *needs_native_bridge = ns->IsBridged();
-  return handle;
+  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.
diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp
index 58ac686..a81fddf 100644
--- a/libnativeloader/native_loader_namespace.cpp
+++ b/libnativeloader/native_loader_namespace.cpp
@@ -28,6 +28,9 @@
 
 #include "nativeloader/dlext_namespaces.h"
 
+using android::base::Error;
+using android::base::Errorf;
+
 namespace android {
 
 namespace {
@@ -35,41 +38,55 @@
 constexpr const char* kDefaultNamespaceName = "default";
 constexpr const char* kPlatformNamespaceName = "platform";
 
-}  // namespace
-
-NativeLoaderNamespace NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
-                                                                  bool is_bridged) {
-  if (!is_bridged) {
-    return NativeLoaderNamespace(name, android_get_exported_namespace(name.c_str()));
-  } else {
-    return NativeLoaderNamespace(name, NativeBridgeGetExportedNamespace(name.c_str()));
+std::string GetLinkerError(bool is_bridged) {
+  const char* msg = is_bridged ? NativeBridgeGetError() : dlerror();
+  if (msg == nullptr) {
+    return "no error";
   }
+  return std::string(msg);
 }
 
-char* NativeLoaderNamespace::GetError() const {
-  if (!IsBridged()) {
-    return strdup(dlerror());
+}  // 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 {
-    return strdup(NativeBridgeGetError());
+    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.
-NativeLoaderNamespace NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
-  NativeLoaderNamespace ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
-  if (ns.IsNil()) {
-    ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
+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));
   }
-  return ns;
 }
 
-NativeLoaderNamespace NativeLoaderNamespace::Create(const std::string& name,
-                                                    const std::string& search_paths,
-                                                    const std::string& permitted_paths,
-                                                    const NativeLoaderNamespace* parent,
-                                                    bool is_shared, bool is_greylist_enabled) {
+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();
@@ -78,10 +95,23 @@
   }
 
   // Fall back to the platform namespace if no parent is set.
-  const NativeLoaderNamespace& effective_parent =
-      parent != nullptr ? *parent : GetPlatformNamespace(is_bridged);
+  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;
   }
@@ -93,37 +123,56 @@
     android_namespace_t* raw =
         android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
                                  permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
-    return NativeLoaderNamespace(name, raw);
+    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());
-    return NativeLoaderNamespace(name, raw);
+    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);
 }
 
-bool NativeLoaderNamespace::Link(const NativeLoaderNamespace& target,
-                                 const std::string& shared_libs) const {
+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()) {
-    return android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
-                                   shared_libs.c_str());
+    if (android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
+                                shared_libs.c_str())) {
+      return {};
+    }
   } else {
-    return NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
-                                      target.ToRawNativeBridgeNamespace(), shared_libs.c_str());
+    if (NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
+                                   target.ToRawNativeBridgeNamespace(), shared_libs.c_str())) {
+      return {};
+    }
   }
+  return Error() << GetLinkerError(IsBridged());
 }
 
-void* NativeLoaderNamespace::Load(const char* lib_name) const {
+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();
-    return android_dlopen_ext(lib_name, RTLD_NOW, &extinfo);
+    void* handle = android_dlopen_ext(lib_name, RTLD_NOW, &extinfo);
+    if (handle != nullptr) {
+      return handle;
+    }
   } else {
-    return NativeBridgeLoadLibraryExt(lib_name, RTLD_NOW, this->ToRawNativeBridgeNamespace());
+    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
index 71e4247..7200ee7 100644
--- a/libnativeloader/native_loader_namespace.h
+++ b/libnativeloader/native_loader_namespace.h
@@ -21,22 +21,26 @@
 #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:
-  // TODO(return with errors)
-  static NativeLoaderNamespace Create(const std::string& name, const std::string& search_paths,
-                                      const std::string& permitted_paths,
-                                      const NativeLoaderNamespace* parent, bool is_shared,
-                                      bool is_greylist_enabled);
+  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;
@@ -47,16 +51,13 @@
 
   std::string name() const { return name_; }
   bool IsBridged() const { return raw_.index() == 1; }
-  bool IsNil() const {
-    return IsBridged() ? std::get<1>(raw_) == nullptr : std::get<0>(raw_) == nullptr;
-  }
 
-  bool Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
-  void* Load(const char* lib_name) const;
-  char* GetError() const;
+  Result<void> Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
+  Result<void*> Load(const char* lib_name) const;
 
-  static NativeLoaderNamespace GetExportedNamespace(const std::string& name, bool is_bridged);
-  static NativeLoaderNamespace GetPlatformNamespace(bool is_bridged);
+  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)
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
new file mode 100644
index 0000000..75255b6
--- /dev/null
+++ b/libnativeloader/native_loader_test.cpp
@@ -0,0 +1,663 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
index c205eb1..3694360 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -26,6 +26,7 @@
 #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>
 
@@ -33,7 +34,11 @@
 
 namespace android::nativeloader {
 
-using namespace std::string_literals;
+using namespace internal;
+using namespace ::std::string_literals;
+using android::base::ErrnoError;
+using android::base::Errorf;
+using android::base::Result;
 
 namespace {
 
@@ -51,6 +56,8 @@
 
 constexpr const char* kRuntimeApexLibPath = "/apex/com.android.runtime/" 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");
@@ -89,55 +96,21 @@
   file_name->insert(insert_pos, vndk_version_str());
 }
 
-const std::function<bool(const std::string&, std::string*)> always_true =
-    [](const std::string&, std::string*) { return true; };
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+    [](const struct ConfigEntry&) -> Result<bool> { return true; };
 
-bool ReadConfig(const std::string& configFile, std::vector<std::string>* sonames,
-                const std::function<bool(const std::string& /* soname */,
-                                         std::string* /* error_msg */)>& check_soname,
-                std::string* error_msg = nullptr) {
-  // Read list of public native libraries from the config file.
+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)) {
-    if (error_msg) *error_msg = strerror(errno);
-    return false;
+    return ErrnoError();
   }
-
-  std::vector<std::string> lines = base::Split(file_content, "\n");
-
-  for (auto& line : lines) {
-    auto trimmed_line = base::Trim(line);
-    if (trimmed_line[0] == '#' || trimmed_line.empty()) {
-      continue;
-    }
-    size_t space_pos = trimmed_line.rfind(' ');
-    if (space_pos != std::string::npos) {
-      std::string type = trimmed_line.substr(space_pos + 1);
-      if (type != "32" && type != "64") {
-        if (error_msg) *error_msg = "Malformed line: " + line;
-        return false;
-      }
-#if defined(__LP64__)
-      // Skip 32 bit public library.
-      if (type == "32") {
-        continue;
-      }
-#else
-      // Skip 64 bit public library.
-      if (type == "64") {
-        continue;
-      }
-#endif
-      trimmed_line.resize(space_pos);
-    }
-
-    if (check_soname(trimmed_line, error_msg)) {
-      sonames->push_back(trimmed_line);
-    } else {
-      return false;
-    }
+  Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
+  if (!result) {
+    return Errorf("Cannot parse {}: {}", configFile, result.error().message());
   }
-  return true;
+  return result;
 }
 
 void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
@@ -160,41 +133,47 @@
             "Error extracting company name from public native library list file path \"%s\"",
             config_file_path.c_str());
 
-        std::string error_msg;
-
-        LOG_ALWAYS_FATAL_IF(
-            !ReadConfig(config_file_path, sonames,
-                        [&company_name](const std::string& soname, std::string* error_msg) {
-                          if (android::base::StartsWith(soname, "lib") &&
-                              android::base::EndsWith(soname, "." + company_name + ".so")) {
-                            return true;
-                          } else {
-                            *error_msg = "Library name \"" + soname +
-                                         "\" does not end with the company name: " + company_name +
-                                         ".";
-                            return false;
-                          }
-                        },
-                        &error_msg),
-            "Error reading public native library list from \"%s\": %s", config_file_path.c_str(),
-            error_msg.c_str());
+        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() {
+static std::string InitDefaultPublicLibraries(bool for_preload) {
   std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
-  std::vector<std::string> sonames;
-  std::string error_msg;
-  LOG_ALWAYS_FATAL_IF(!ReadConfig(config_file, &sonames, always_true, &error_msg),
-                      "Error reading public native library list from \"%s\": %s",
-                      config_file.c_str(), error_msg.c_str());
+  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));
+    std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames));
   }
 
   // Remove the public libs in the runtime namespace.
@@ -214,12 +193,18 @@
       continue;
     }
 
-    auto it = std::find(sonames.begin(), sonames.end(), lib_name);
-    if (it != sonames.end()) {
-      sonames.erase(it);
+    auto it = std::find(sonames->begin(), sonames->end(), lib_name);
+    if (it != sonames->end()) {
+      sonames->erase(it);
     }
   }
-  return android::base::Join(sonames, ':');
+
+  // 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 InitRuntimePublicLibraries() {
@@ -235,9 +220,11 @@
 
 static std::string InitVendorPublicLibraries() {
   // This file is optional, quietly ignore if the file does not exist.
-  std::vector<std::string> sonames;
-  ReadConfig(kVendorPublicLibrariesFile, &sonames, always_true, nullptr);
-  return android::base::Join(sonames, ':');
+  auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
+  if (!sonames) {
+    return "";
+  }
+  return android::base::Join(*sonames, ':');
 }
 
 // read /system/etc/public.libraries-<companyname>.txt and
@@ -254,23 +241,38 @@
 static std::string InitLlndkLibraries() {
   std::string config_file = kLlndkLibrariesFile;
   InsertVndkVersionStr(&config_file);
-  std::vector<std::string> sonames;
-  ReadConfig(config_file, &sonames, always_true, nullptr);
-  return android::base::Join(sonames, ':');
+  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);
-  std::vector<std::string> sonames;
-  ReadConfig(config_file, &sonames, always_true, nullptr);
-  return android::base::Join(sonames, ':');
+  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();
+  static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
   return list;
 }
 
@@ -289,6 +291,11 @@
   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;
@@ -299,4 +306,61 @@
   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
index 9b6dea8..2de4172 100644
--- a/libnativeloader/public_libraries.h
+++ b/libnativeloader/public_libraries.h
@@ -15,18 +15,42 @@
  */
 #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();
 
-};  // namespace android::nativeloader
+// 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/demangle/.clang-format b/libpackagelistparser/.clang-format
similarity index 100%
rename from demangle/.clang-format
rename to libpackagelistparser/.clang-format
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index c38594a..0740e7d 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,12 +1,7 @@
 cc_library {
-
     name: "libpackagelistparser",
     recovery_available: true,
-    srcs: ["packagelistparser.c"],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
+    srcs: ["packagelistparser.cpp"],
     shared_libs: ["liblog"],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
@@ -15,3 +10,13 @@
         misc_undefined: ["integer"],
     },
 }
+
+cc_test {
+    name: "libpackagelistparser_test",
+    srcs: ["packagelistparser_test.cpp"],
+    shared_libs: [
+        "libbase",
+        "libpackagelistparser",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
index 3cb6b9a..e89cb54 100644
--- a/libpackagelistparser/include/packagelistparser/packagelistparser.h
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -1,94 +1,81 @@
 /*
- * Copyright 2015, Intel Corporation
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
- * Written by William Roberts <william.c.roberts@intel.com>
- *
- * This is a parser library for parsing the packages.list file generated
- * by PackageManager service.
- *
- * This simple parser is sensitive to format changes in
- * frameworks/base/services/core/java/com/android/server/pm/Settings.java
- * A dependency note has been added to that file to correct
- * this parser.
  */
 
-#ifndef PACKAGELISTPARSER_H_
-#define PACKAGELISTPARSER_H_
+#pragma once
 
 #include <stdbool.h>
-#include <sys/cdefs.h>
 #include <sys/types.h>
 
 __BEGIN_DECLS
 
-/** The file containing the list of installed packages on the system */
-#define PACKAGES_LIST_FILE  "/data/system/packages.list"
+typedef struct gid_list {
+  /** Number of gids. */
+  size_t cnt;
 
-typedef struct pkg_info pkg_info;
-typedef struct gid_list gid_list;
+  /** Array of gids. */
+  gid_t* gids;
+} gid_list;
 
-struct gid_list {
-    size_t cnt;
-    gid_t *gids;
-};
+typedef struct pkg_info {
+  /** Package name like "com.android.blah". */
+  char* name;
 
-struct pkg_info {
-    char *name;
-    uid_t uid;
-    bool debuggable;
-    char *data_dir;
-    char *seinfo;
-    gid_list gids;
-    void *private_data;
-    bool profileable_from_shell;
-    long version_code;
-};
+  /** Package uid like 10014. */
+  uid_t uid;
+
+  /** Package's AndroidManifest.xml debuggable flag. */
+  bool debuggable;
+
+  /** Package data directory like "/data/user/0/com.android.blah" */
+  char* data_dir;
+
+  /** Package SELinux info. */
+  char* seinfo;
+
+  /** Package's list of gids. */
+  gid_list gids;
+
+  /** Spare pointer for the caller to stash extra data off. */
+  void* private_data;
+
+  /** Package's AndroidManifest.xml profileable flag. */
+  bool profileable_from_shell;
+
+  /** Package's AndroidManifest.xml version code. */
+  long version_code;
+} pkg_info;
 
 /**
- * Callback function to be used by packagelist_parse() routine.
- * @param info
- *  The parsed package information
- * @param userdata
- *  The supplied userdata pointer to packagelist_parse()
- * @return
- *  true to keep processing, false to stop.
+ * Parses the system's default package list.
+ * Invokes `callback` once for each package.
+ * The callback owns the `pkg_info*` and should call packagelist_free().
+ * The callback should return `false` to exit early or `true` to continue.
  */
-typedef bool (*pfn_on_package)(pkg_info *info, void *userdata);
+bool packagelist_parse(bool (*callback)(pkg_info* info, void* user_data), void* user_data);
 
 /**
- * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on
- * each entry found. Once the callback is invoked, ownership of the pkg_info pointer
- * is passed to the callback routine, thus they are required to perform any cleanup
- * desired.
- * @param callback
- *  The callback function called on each parsed line of the packages list.
- * @param userdata
- *  An optional userdata supplied pointer to pass to the callback function.
- * @return
- *  true on success false on failure.
+ * Parses the given package list.
+ * Invokes `callback` once for each package.
+ * The callback owns the `pkg_info*` and should call packagelist_free().
+ * The callback should return `false` to exit early or `true` to continue.
  */
-extern bool packagelist_parse(pfn_on_package callback, void *userdata);
+bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info* info, void* user_data),
+                            void* user_data);
 
-/**
- * Frees a pkg_info structure.
- * @param info
- *  The struct to free
- */
-extern void packagelist_free(pkg_info *info);
+/** Frees the given `pkg_info`. */
+void packagelist_free(pkg_info* info);
 
 __END_DECLS
-
-#endif /* PACKAGELISTPARSER_H_ */
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
deleted file mode 100644
index edc533c..0000000
--- a/libpackagelistparser/packagelistparser.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright 2015, Intel Corporation
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Written by William Roberts <william.c.roberts@intel.com>
- *
- */
-
-#define LOG_TAG "packagelistparser"
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/limits.h>
-
-#include <log/log.h>
-#include <packagelistparser/packagelistparser.h>
-
-#define CLOGE(fmt, ...) \
-    do {\
-        IF_ALOGE() {\
-            ALOGE(fmt, ##__VA_ARGS__);\
-        }\
-    } while(0)
-
-static size_t get_gid_cnt(const char *gids)
-{
-    size_t cnt;
-
-    if (*gids == '\0') {
-        return 0;
-    }
-
-    if (!strcmp(gids, "none")) {
-        return 0;
-    }
-
-    for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
-        ;
-
-    return cnt;
-}
-
-static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
-{
-    gid_t gid;
-    char* token;
-    char *endptr;
-    size_t cmp = 0;
-
-    while ((token = strsep(&gids, ",\r\n"))) {
-
-        if (cmp > *cnt) {
-            return false;
-        }
-
-        gid = strtoul(token, &endptr, 10);
-        if (*endptr != '\0') {
-            return false;
-        }
-
-        /*
-         * if unsigned long is greater than size of gid_t,
-         * prevent a truncation based roll-over
-         */
-        if (gid > GID_MAX) {
-            CLOGE("A gid in field \"gid list\" greater than GID_MAX");
-            return false;
-        }
-
-        gid_list[cmp++] = gid;
-    }
-    return true;
-}
-
-extern bool packagelist_parse(pfn_on_package callback, void *userdata)
-{
-
-    FILE *fp;
-    char *cur;
-    char *next;
-    char *endptr;
-    unsigned long tmp;
-    ssize_t bytesread;
-
-    bool rc = false;
-    char *buf = NULL;
-    size_t buflen = 0;
-    unsigned long lineno = 1;
-    const char *errmsg = NULL;
-    struct pkg_info *pkg_info = NULL;
-
-    fp = fopen(PACKAGES_LIST_FILE, "re");
-    if (!fp) {
-        CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
-                strerror(errno));
-        return false;
-    }
-
-    while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
-
-        pkg_info = calloc(1, sizeof(*pkg_info));
-        if (!pkg_info) {
-            goto err;
-        }
-
-        next = buf;
-
-        cur = strsep(&next, " \t\r\n");
-        if (!cur) {
-            errmsg = "Could not get next token for \"package name\"";
-            goto err;
-        }
-
-        pkg_info->name = strdup(cur);
-        if (!pkg_info->name) {
-            goto err;
-        }
-
-        cur = strsep(&next, " \t\r\n");
-        if (!cur) {
-            errmsg = "Could not get next token for field \"uid\"";
-            goto err;
-        }
-
-        tmp = strtoul(cur, &endptr, 10);
-        if (*endptr != '\0') {
-            errmsg = "Could not convert field \"uid\" to integer value";
-            goto err;
-        }
-
-        /*
-         * if unsigned long is greater than size of uid_t,
-         * prevent a truncation based roll-over
-         */
-        if (tmp > UID_MAX) {
-            errmsg = "Field \"uid\" greater than UID_MAX";
-            goto err;
-        }
-
-        pkg_info->uid = (uid_t) tmp;
-
-        cur = strsep(&next, " \t\r\n");
-        if (!cur) {
-            errmsg = "Could not get next token for field \"debuggable\"";
-            goto err;
-        }
-
-        tmp = strtoul(cur, &endptr, 10);
-        if (*endptr != '\0') {
-            errmsg = "Could not convert field \"debuggable\" to integer value";
-            goto err;
-        }
-
-        /* should be a valid boolean of 1 or 0 */
-        if (!(tmp == 0 || tmp == 1)) {
-            errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
-            goto err;
-        }
-
-        pkg_info->debuggable = (bool) tmp;
-
-        cur = strsep(&next, " \t\r\n");
-        if (!cur) {
-            errmsg = "Could not get next token for field \"data dir\"";
-            goto err;
-        }
-
-        pkg_info->data_dir = strdup(cur);
-        if (!pkg_info->data_dir) {
-            goto err;
-        }
-
-        cur = strsep(&next, " \t\r\n");
-        if (!cur) {
-            errmsg = "Could not get next token for field \"seinfo\"";
-            goto err;
-        }
-
-        pkg_info->seinfo = strdup(cur);
-        if (!pkg_info->seinfo) {
-            goto err;
-        }
-
-        cur = strsep(&next, " \t\r\n");
-        if (!cur) {
-            errmsg = "Could not get next token for field \"gid(s)\"";
-            goto err;
-        }
-
-        /*
-         * Parse the gid list, could be in the form of none, single gid or list:
-         * none
-         * gid
-         * gid, gid ...
-         */
-        pkg_info->gids.cnt = get_gid_cnt(cur);
-        if (pkg_info->gids.cnt > 0) {
-
-            pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
-            if (!pkg_info->gids.gids) {
-                goto err;
-            }
-
-            rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
-            if (!rc) {
-                errmsg = "Could not parse field \"gid list\"";
-                goto err;
-            }
-        }
-
-        cur = strsep(&next, " \t\r\n");
-        if (cur) {
-            tmp = strtoul(cur, &endptr, 10);
-            if (*endptr != '\0') {
-                errmsg = "Could not convert field \"profileable_from_shell\" to integer value";
-                goto err;
-            }
-
-            /* should be a valid boolean of 1 or 0 */
-            if (!(tmp == 0 || tmp == 1)) {
-                errmsg = "Field \"profileable_from_shell\" is not 0 or 1 boolean value";
-                goto err;
-            }
-
-            pkg_info->profileable_from_shell = (bool)tmp;
-        }
-        cur = strsep(&next, " \t\r\n");
-        if (cur) {
-            tmp = strtoul(cur, &endptr, 10);
-            if (*endptr != '\0') {
-                errmsg = "Could not convert field \"versionCode\" to integer value";
-                goto err;
-            }
-            pkg_info->version_code = tmp;
-        }
-
-        rc = callback(pkg_info, userdata);
-        if (rc == false) {
-            /*
-             * We do not log this as this can be intentional from
-             * callback to abort processing. We go to out to not
-             * free the pkg_info
-             */
-            rc = true;
-            goto out;
-        }
-        lineno++;
-    }
-
-    rc = true;
-
-out:
-    free(buf);
-    fclose(fp);
-    return rc;
-
-err:
-    if (errmsg) {
-        CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
-                PACKAGES_LIST_FILE, lineno, errmsg);
-    }
-    rc = false;
-    packagelist_free(pkg_info);
-    goto out;
-}
-
-void packagelist_free(pkg_info *info)
-{
-    if (info) {
-        free(info->name);
-        free(info->data_dir);
-        free(info->seinfo);
-        free(info->gids.gids);
-        free(info);
-    }
-}
diff --git a/libpackagelistparser/packagelistparser.cpp b/libpackagelistparser/packagelistparser.cpp
new file mode 100644
index 0000000..59c3a74
--- /dev/null
+++ b/libpackagelistparser/packagelistparser.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "packagelistparser"
+
+#include <packagelistparser/packagelistparser.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/limits.h>
+
+#include <memory>
+
+#include <log/log.h>
+
+static bool parse_gids(const char* path, size_t line_number, const char* gids, pkg_info* info) {
+  // Nothing to do?
+  if (!gids || !strcmp(gids, "none")) return true;
+
+  // How much space do we need?
+  info->gids.cnt = 1;
+  for (const char* p = gids; *p; ++p) {
+    if (*p == ',') ++info->gids.cnt;
+  }
+
+  // Allocate the space.
+  info->gids.gids = new gid_t[info->gids.cnt];
+  if (!info->gids.gids) return false;
+
+  // And parse the individual gids.
+  size_t i = 0;
+  while (true) {
+    char* end;
+    unsigned long gid = strtoul(gids, &end, 10);
+    if (gid > GID_MAX) {
+      ALOGE("%s:%zu: gid %lu > GID_MAX", path, line_number, gid);
+      return false;
+    }
+
+    if (i >= info->gids.cnt) return false;
+    info->gids.gids[i++] = gid;
+
+    if (*end == '\0') return true;
+    if (*end != ',') return false;
+    gids = end + 1;
+  }
+  return true;
+}
+
+static bool parse_line(const char* path, size_t line_number, const char* line, pkg_info* info) {
+  unsigned long uid;
+  int debuggable;
+  char* gid_list;
+  int profileable_from_shell = 0;
+
+  int fields =
+      sscanf(line, "%ms %lu %d %ms %ms %ms %d %ld", &info->name, &uid, &debuggable, &info->data_dir,
+             &info->seinfo, &gid_list, &profileable_from_shell, &info->version_code);
+
+  // Handle the more complicated gids field and free the temporary string.
+  bool gids_okay = parse_gids(path, line_number, gid_list, info);
+  free(gid_list);
+  if (!gids_okay) return false;
+
+  // Did we see enough fields to be getting on with?
+  // The final fields are optional (and not usually present).
+  if (fields < 6) {
+    ALOGE("%s:%zu: too few fields in line", path, line_number);
+    return false;
+  }
+
+  // Extra validation.
+  if (uid > UID_MAX) {
+    ALOGE("%s:%zu: uid %lu > UID_MAX", path, line_number, uid);
+    return false;
+  }
+  info->uid = uid;
+
+  // Integer to bool conversions.
+  info->debuggable = debuggable;
+  info->profileable_from_shell = profileable_from_shell;
+
+  return true;
+}
+
+bool packagelist_parse_file(const char* path, bool (*callback)(pkg_info*, void*), void* user_data) {
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path, "re"), &fclose);
+  if (!fp) {
+    ALOGE("couldn't open '%s': %s", path, strerror(errno));
+    return false;
+  }
+
+  size_t line_number = 0;
+  char* line = nullptr;
+  size_t allocated_length = 0;
+  while (getline(&line, &allocated_length, fp.get()) > 0) {
+    ++line_number;
+    std::unique_ptr<pkg_info, decltype(&packagelist_free)> info(
+        static_cast<pkg_info*>(calloc(1, sizeof(pkg_info))), &packagelist_free);
+    if (!info) {
+      ALOGE("%s:%zu: couldn't allocate pkg_info", path, line_number);
+      return false;
+    }
+
+    if (!parse_line(path, line_number, line, info.get())) return false;
+
+    if (!callback(info.release(), user_data)) break;
+  }
+  free(line);
+  return true;
+}
+
+bool packagelist_parse(bool (*callback)(pkg_info*, void*), void* user_data) {
+  return packagelist_parse_file("/data/system/packages.list", callback, user_data);
+}
+
+void packagelist_free(pkg_info* info) {
+  if (!info) return;
+
+  free(info->name);
+  free(info->data_dir);
+  free(info->seinfo);
+  delete[] info->gids.gids;
+  free(info);
+}
diff --git a/libpackagelistparser/packagelistparser_test.cpp b/libpackagelistparser/packagelistparser_test.cpp
new file mode 100644
index 0000000..76cb886
--- /dev/null
+++ b/libpackagelistparser/packagelistparser_test.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <packagelistparser/packagelistparser.h>
+
+#include <memory>
+
+#include <android-base/file.h>
+
+#include <gtest/gtest.h>
+
+TEST(packagelistparser, smoke) {
+  TemporaryFile tf;
+  android::base::WriteStringToFile(
+      // No gids.
+      "com.test.a0 10014 0 /data/user/0/com.test.a0 platform:privapp:targetSdkVersion=19 none\n"
+      // One gid.
+      "com.test.a1 10007 1 /data/user/0/com.test.a1 platform:privapp:targetSdkVersion=21 1023\n"
+      // Multiple gids.
+      "com.test.a2 10011 0 /data/user/0/com.test.a2 media:privapp:targetSdkVersion=30 "
+      "2001,1065,1023,3003,3007,1024\n"
+      // The two new fields (profileable flag and version code).
+      "com.test.a3 10022 0 /data/user/0/com.test.a3 selabel:blah none 1 123\n",
+      tf.path);
+
+  std::vector<pkg_info*> packages;
+  packagelist_parse_file(
+      tf.path,
+      [](pkg_info* info, void* user_data) -> bool {
+        reinterpret_cast<std::vector<pkg_info*>*>(user_data)->push_back(info);
+        return true;
+      },
+      &packages);
+
+  ASSERT_EQ(4U, packages.size());
+
+  ASSERT_STREQ("com.test.a0", packages[0]->name);
+  ASSERT_EQ(10014, packages[0]->uid);
+  ASSERT_FALSE(packages[0]->debuggable);
+  ASSERT_STREQ("/data/user/0/com.test.a0", packages[0]->data_dir);
+  ASSERT_STREQ("platform:privapp:targetSdkVersion=19", packages[0]->seinfo);
+  ASSERT_EQ(0U, packages[0]->gids.cnt);
+  ASSERT_FALSE(packages[0]->profileable_from_shell);
+  ASSERT_EQ(0, packages[0]->version_code);
+
+  ASSERT_STREQ("com.test.a1", packages[1]->name);
+  ASSERT_EQ(10007, packages[1]->uid);
+  ASSERT_TRUE(packages[1]->debuggable);
+  ASSERT_STREQ("/data/user/0/com.test.a1", packages[1]->data_dir);
+  ASSERT_STREQ("platform:privapp:targetSdkVersion=21", packages[1]->seinfo);
+  ASSERT_EQ(1U, packages[1]->gids.cnt);
+  ASSERT_EQ(1023U, packages[1]->gids.gids[0]);
+  ASSERT_FALSE(packages[0]->profileable_from_shell);
+  ASSERT_EQ(0, packages[0]->version_code);
+
+  ASSERT_STREQ("com.test.a2", packages[2]->name);
+  ASSERT_EQ(10011, packages[2]->uid);
+  ASSERT_FALSE(packages[2]->debuggable);
+  ASSERT_STREQ("/data/user/0/com.test.a2", packages[2]->data_dir);
+  ASSERT_STREQ("media:privapp:targetSdkVersion=30", packages[2]->seinfo);
+  ASSERT_EQ(6U, packages[2]->gids.cnt);
+  ASSERT_EQ(2001U, packages[2]->gids.gids[0]);
+  ASSERT_EQ(1024U, packages[2]->gids.gids[5]);
+  ASSERT_FALSE(packages[0]->profileable_from_shell);
+  ASSERT_EQ(0, packages[0]->version_code);
+
+  ASSERT_STREQ("com.test.a3", packages[3]->name);
+  ASSERT_EQ(10022, packages[3]->uid);
+  ASSERT_FALSE(packages[3]->debuggable);
+  ASSERT_STREQ("/data/user/0/com.test.a3", packages[3]->data_dir);
+  ASSERT_STREQ("selabel:blah", packages[3]->seinfo);
+  ASSERT_EQ(0U, packages[3]->gids.cnt);
+  ASSERT_TRUE(packages[3]->profileable_from_shell);
+  ASSERT_EQ(123, packages[3]->version_code);
+
+  for (auto& package : packages) packagelist_free(package);
+}
+
+TEST(packagelistparser, early_exit) {
+  TemporaryFile tf;
+  android::base::WriteStringToFile(
+      "com.test.a0 1 0 / a none\n"
+      "com.test.a1 1 0 / a none\n"
+      "com.test.a2 1 0 / a none\n",
+      tf.path);
+
+  std::vector<pkg_info*> packages;
+  packagelist_parse_file(
+      tf.path,
+      [](pkg_info* info, void* user_data) -> bool {
+        std::vector<pkg_info*>* p = reinterpret_cast<std::vector<pkg_info*>*>(user_data);
+        p->push_back(info);
+        return p->size() < 2;
+      },
+      &packages);
+
+  ASSERT_EQ(2U, packages.size());
+
+  ASSERT_STREQ("com.test.a0", packages[0]->name);
+  ASSERT_STREQ("com.test.a1", packages[1]->name);
+
+  for (auto& package : packages) packagelist_free(package);
+}
+
+TEST(packagelistparser, system_package_list) {
+  // Check that we can actually read the packages.list installed on the device.
+  std::vector<pkg_info*> packages;
+  packagelist_parse(
+      [](pkg_info* info, void* user_data) -> bool {
+        reinterpret_cast<std::vector<pkg_info*>*>(user_data)->push_back(info);
+        return true;
+      },
+      &packages);
+  // Not much we can say for sure about what we expect, other than that there
+  // are likely to be lots of packages...
+  ASSERT_GT(packages.size(), 10U);
+}
+
+TEST(packagelistparser, packagelist_free_nullptr) {
+  packagelist_free(nullptr);
+}
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 15f03d0..0c9a2b8 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -34,6 +34,7 @@
     host_supported: true,
     srcs: [
         "process.cpp",
+        "process_map.cpp",
     ],
 
     local_include_dirs: ["include"],
@@ -58,6 +59,7 @@
     name: "libprocinfo_test",
     defaults: ["libprocinfo_defaults"],
     host_supported: true,
+    isolated: true,
     srcs: [
         "process_test.cpp",
         "process_map_test.cpp",
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index b6ec3cb..569a022 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -176,5 +176,9 @@
                const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
 }
 
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+                          const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+                                                   const char*)>& callback);
+
 } /* namespace procinfo */
 } /* namespace android */
diff --git a/libprocinfo/process_map.cpp b/libprocinfo/process_map.cpp
new file mode 100644
index 0000000..5e240b9
--- /dev/null
+++ b/libprocinfo/process_map.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <procinfo/process_map.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <procinfo/process.h>
+
+namespace android {
+namespace procinfo {
+
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+                          const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+                                                   const char*)>& callback) {
+  if (buffer == nullptr || buffer_size == 0) {
+    return false;
+  }
+
+  int fd = open(map_file, O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    return false;
+  }
+
+  char* char_buffer = reinterpret_cast<char*>(buffer);
+  size_t start = 0;
+  size_t read_bytes = 0;
+  char* line = nullptr;
+  bool read_complete = false;
+  while (true) {
+    ssize_t bytes =
+        TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
+    if (bytes <= 0) {
+      if (read_bytes == 0) {
+        close(fd);
+        return bytes == 0;
+      }
+      // Treat the last piece of data as the last line.
+      char_buffer[start + read_bytes] = '\n';
+      bytes = 1;
+      read_complete = true;
+    }
+    read_bytes += bytes;
+
+    while (read_bytes > 0) {
+      char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
+      if (newline == nullptr) {
+        break;
+      }
+      *newline = '\0';
+      line = &char_buffer[start];
+      start = newline - char_buffer + 1;
+      read_bytes -= newline - line + 1;
+
+      // Ignore the return code, errors are okay.
+      ReadMapFileContent(line, callback);
+    }
+
+    if (read_complete) {
+      close(fd);
+      return true;
+    }
+
+    if (start == 0 && read_bytes == buffer_size - 1) {
+      // The buffer provided is too small to contain this line, give up
+      // and indicate failure.
+      close(fd);
+      return false;
+    }
+
+    // Copy any leftover data to the front  of the buffer.
+    if (start > 0) {
+      if (read_bytes > 0) {
+        memmove(char_buffer, &char_buffer[start], read_bytes);
+      }
+      start = 0;
+    }
+  }
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 562d864..b1bdc08 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -16,9 +16,14 @@
 
 #include <procinfo/process_map.h>
 
+#include <inttypes.h>
+#include <sys/mman.h>
+
 #include <string>
+#include <vector>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 
 #include <gtest/gtest.h>
 
@@ -63,3 +68,215 @@
   ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
   ASSERT_GT(maps.size(), 0u);
 }
+
+extern "C" void malloc_disable();
+extern "C" void malloc_enable();
+
+struct TestMapInfo {
+  TestMapInfo() = default;
+  TestMapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+              const char* new_name)
+      : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode) {
+    strcpy(name, new_name);
+  }
+  uint64_t start = 0;
+  uint64_t end = 0;
+  uint16_t flags = 0;
+  uint64_t pgoff = 0;
+  ino_t inode = 0;
+  char name[100] = {};
+};
+
+void VerifyReadMapFileAsyncSafe(const char* maps_data,
+                                const std::vector<TestMapInfo>& expected_info) {
+  TemporaryFile tf;
+  ASSERT_TRUE(android::base::WriteStringToFd(maps_data, tf.fd));
+
+  std::vector<TestMapInfo> saved_info(expected_info.size());
+  size_t num_maps = 0;
+
+  auto callback = [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+                      const char* name) {
+    if (num_maps != saved_info.size()) {
+      TestMapInfo& saved = saved_info[num_maps];
+      saved.start = start;
+      saved.end = end;
+      saved.flags = flags;
+      saved.pgoff = pgoff;
+      saved.inode = inode;
+      strcpy(saved.name, name);
+    }
+    num_maps++;
+  };
+
+  std::vector<char> buffer(64 * 1024);
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  bool parsed =
+      android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer.data(), buffer.size(), callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_TRUE(parsed) << "Parsing of data failed:\n" << maps_data;
+  ASSERT_EQ(expected_info.size(), num_maps);
+  for (size_t i = 0; i < expected_info.size(); i++) {
+    const TestMapInfo& expected = expected_info[i];
+    const TestMapInfo& saved = saved_info[i];
+    EXPECT_EQ(expected.start, saved.start);
+    EXPECT_EQ(expected.end, saved.end);
+    EXPECT_EQ(expected.flags, saved.flags);
+    EXPECT_EQ(expected.pgoff, saved.pgoff);
+    EXPECT_EQ(expected.inode, saved.inode);
+    EXPECT_STREQ(expected.name, saved.name);
+  }
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_invalid) {
+  std::vector<TestMapInfo> expected_info;
+
+  VerifyReadMapFileAsyncSafe("12c00000-2ac00000", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single) {
+  std::vector<TestMapInfo> expected_info;
+  expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+                             "/lib/fake.so");
+
+  VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so",
+                             expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_with_newline) {
+  std::vector<TestMapInfo> expected_info;
+  expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+                             "/lib/fake.so");
+
+  VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so\n",
+                             expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_no_library) {
+  std::vector<TestMapInfo> expected_info;
+  expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 0xb00, 101, "");
+
+  VerifyReadMapFileAsyncSafe("a0000-c0000 rwxp 00000b00 00:05 101", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple) {
+  std::vector<TestMapInfo> expected_info;
+  expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 1, 100, "");
+  expected_info.emplace_back(0xd0000, 0xe0000, PROT_READ, 2, 101, "/lib/libsomething1.so");
+  expected_info.emplace_back(0xf0000, 0x100000, PROT_WRITE, 3, 102, "/lib/libsomething2.so");
+  expected_info.emplace_back(0x110000, 0x120000, PROT_EXEC, 4, 103, "[anon:something or another]");
+
+  std::string map_data =
+      "0a0000-0c0000 rwxp 00000001 00:05 100\n"
+      "0d0000-0e0000 r--p 00000002 00:05 101  /lib/libsomething1.so\n"
+      "0f0000-100000 -w-p 00000003 00:05 102  /lib/libsomething2.so\n"
+      "110000-120000 --xp 00000004 00:05 103  [anon:something or another]\n";
+
+  VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple_reads) {
+  std::vector<TestMapInfo> expected_info;
+  std::string map_data;
+  uint64_t start = 0xa0000;
+  for (size_t i = 0; i < 10000; i++) {
+    map_data += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r--p %zx 01:20 %zu fake.so\n",
+                                            start, start + 0x1000, i, 1000 + i);
+    expected_info.emplace_back(start, start + 0x1000, PROT_READ, i, 1000 + i, "fake.so");
+  }
+
+  VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_nullptr) {
+  size_t num_calls = 0;
+  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", nullptr, 10, callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_FALSE(parsed);
+  EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_size_zero) {
+  size_t num_calls = 0;
+  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  char buffer[10];
+  bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, 0, callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_FALSE(parsed);
+  EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_no_calls) {
+  size_t num_calls = 0;
+  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  char buffer[10];
+  bool parsed =
+      android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_FALSE(parsed);
+  EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_could_parse) {
+  TemporaryFile tf;
+  ASSERT_TRUE(android::base::WriteStringToFd(
+      "0a0000-0c0000 rwxp 00000001 00:05 100    /fake/lib.so\n", tf.fd));
+
+  size_t num_calls = 0;
+  auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+  // Any allocations will block after this call.
+  malloc_disable();
+#endif
+
+  char buffer[39];
+  bool parsed = android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+  malloc_enable();
+#endif
+
+  ASSERT_FALSE(parsed);
+  EXPECT_EQ(0UL, num_calls);
+}
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index 5b8179f..c5c4960 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -17,6 +17,7 @@
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
+#include <algorithm>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <limits.h>
@@ -48,13 +49,6 @@
 #define off64_t off_t
 #endif
 
-#define min(a, b)        \
-  ({                     \
-    typeof(a) _a = (a);  \
-    typeof(b) _b = (b);  \
-    (_a < _b) ? _a : _b; \
-  })
-
 #define SPARSE_HEADER_MAJOR_VER 1
 #define SPARSE_HEADER_MINOR_VER 0
 #define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
@@ -231,7 +225,7 @@
   struct output_file_gz* outgz = to_output_file_gz(out);
 
   while (len > 0) {
-    ret = gzwrite(outgz->gz_fd, data, min(len, (unsigned int)INT_MAX));
+    ret = gzwrite(outgz->gz_fd, data, std::min<unsigned int>(len, (unsigned int)INT_MAX));
     if (ret == 0) {
       error("gzwrite %s", gzerror(outgz->gz_fd, nullptr));
       return -1;
@@ -268,7 +262,7 @@
   int ret;
 
   while (off > 0) {
-    to_write = min(off, (int64_t)INT_MAX);
+    to_write = std::min(off, (int64_t)INT_MAX);
     ret = outc->write(outc->priv, nullptr, to_write);
     if (ret < 0) {
       return ret;
@@ -470,7 +464,7 @@
   }
 
   while (len) {
-    write_len = min(len, out->block_size);
+    write_len = std::min(len, out->block_size);
     ret = out->ops->write(out, out->fill_buf, write_len);
     if (ret < 0) {
       return ret;
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index 845a197..b7ada0c 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_STATS_LOG_STATS_EVENT_LIST_H
-#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
+#pragma once
 
 #include <log/log_event_list.h>
 #include <sys/uio.h>
@@ -133,7 +132,6 @@
         return *this;
     }
 
-#if defined(_USING_LIBCXX)
     stats_event_list& operator<<(const std::string& value) {
         int retval = android_log_write_string8_len(ctx, value.data(), value.length());
         if (retval < 0) {
@@ -141,7 +139,6 @@
         }
         return *this;
     }
-#endif
 
     stats_event_list& operator<<(float value) {
         int retval = android_log_write_float32(ctx, value);
@@ -203,7 +200,6 @@
         return ret >= 0;
     }
 
-#if defined(_USING_LIBCXX)
     bool AppendString(const std::string& value) {
         int retval = android_log_write_string8_len(ctx, value.data(), value.length());
         if (retval < 0) {
@@ -219,7 +215,6 @@
         }
         return ret;
     }
-#endif
 
     bool AppendFloat(float value) {
         int retval = android_log_write_float32(ctx, value);
@@ -253,4 +248,3 @@
 };
 
 #endif
-#endif  // ANDROID_STATS_LOG_STATS_EVENT_LIST_H
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 5423de5..73237e6 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -125,10 +125,6 @@
         },
     },
 
-    whole_static_libs: [
-        "libdemangle"
-    ],
-
     static_libs: [
         "libprocinfo",
     ],
@@ -162,6 +158,7 @@
 cc_test {
     name: "libunwindstack_test",
     defaults: ["libunwindstack_flags"],
+    isolated: true,
 
     srcs: [
         "tests/ArmExidxDecodeTest.cpp",
@@ -184,6 +181,7 @@
         "tests/ElfInterfaceTest.cpp",
         "tests/ElfTest.cpp",
         "tests/ElfTestUtils.cpp",
+        "tests/IsolatedSettings.cpp",
         "tests/JitDebugTest.cpp",
         "tests/LocalUnwinderTest.cpp",
         "tests/LogFake.cpp",
@@ -246,6 +244,7 @@
         "tests/files/offline/jit_debug_x86/*",
         "tests/files/offline/jit_map_arm/*",
         "tests/files/offline/gnu_debugdata_arm/*",
+        "tests/files/offline/load_bias_ro_rx_x86_64/*",
         "tests/files/offline/offset_arm/*",
         "tests/files/offline/shared_lib_in_apk_arm64/*",
         "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index f0e4138..bdfee01 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -197,6 +197,7 @@
 template <typename EhdrType, typename PhdrType>
 void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
   uint64_t offset = ehdr.e_phoff;
+  bool first_exec_load_header = true;
   for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
     PhdrType phdr;
     if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
@@ -212,9 +213,11 @@
 
       pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
                                           static_cast<size_t>(phdr.p_memsz)};
-      if (phdr.p_offset == 0) {
-        *load_bias = phdr.p_vaddr;
+      // 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;
       }
+      first_exec_load_header = false;
       break;
     }
 
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index c95f852..7556482 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -27,8 +27,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include <demangle.h>
-
 #include <unwindstack/Elf.h>
 #include <unwindstack/JitDebug.h>
 #include <unwindstack/MapInfo.h>
@@ -40,6 +38,9 @@
 #include <unwindstack/DexFiles.h>
 #endif
 
+// Use the demangler from libc++.
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
+
 namespace unwindstack {
 
 // Inject extra 'virtual' frame that represents the dex pc data.
@@ -330,7 +331,14 @@
   }
 
   if (!frame.function_name.empty()) {
-    data += " (" + demangle(frame.function_name.c_str());
+    char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);
+    if (demangled_name == nullptr) {
+      data += " (" + frame.function_name;
+    } else {
+      data += " (";
+      data += demangled_name;
+      free(demangled_name);
+    }
     if (frame.function_offset != 0) {
       data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
     }
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index cdc927a..f9ee9eb 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -360,7 +360,7 @@
 
   uint64_t load_bias = 0;
   ASSERT_TRUE(elf->Init(&load_bias));
-  EXPECT_EQ(0U, load_bias);
+  EXPECT_EQ(0x1001U, load_bias);
 
   const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
   ASSERT_EQ(1U, pt_loads.size());
diff --git a/logd/LogBufferInterface.cpp b/libunwindstack/tests/IsolatedSettings.cpp
similarity index 60%
copy from logd/LogBufferInterface.cpp
copy to libunwindstack/tests/IsolatedSettings.cpp
index 4b6d363..dbd8bd6 100644
--- a/logd/LogBufferInterface.cpp
+++ b/libunwindstack/tests/IsolatedSettings.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-#include "LogBufferInterface.h"
-#include "LogUtils.h"
+#include <stdint.h>
+#include <stdio.h>
 
-LogBufferInterface::LogBufferInterface() {
-}
-LogBufferInterface::~LogBufferInterface() {
-}
-uid_t LogBufferInterface::pidToUid(pid_t pid) {
-    return android::pidToUid(pid);
-}
-pid_t LogBufferInterface::tidToPid(pid_t tid) {
-    return android::tidToPid(tid);
+extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) {
+  static const char* initial_args[2] = {"--slow_threshold_ms=90000",
+                                        "--deadline_threshold_ms=120000"};
+  *args = initial_args;
+  *num_args = 2;
+  return true;
 }
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 5b4ca7c..6c1cfa2 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -58,7 +58,7 @@
     ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
   }
 
-  static void SetUpTestSuite() {
+  void SetUp() override {
     std::vector<uint8_t> buffer(12288, 0);
     memcpy(buffer.data(), ELFMAG, SELFMAG);
     buffer[EI_CLASS] = ELFCLASS32;
@@ -72,9 +72,7 @@
 
     InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
     InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
-  }
 
-  void SetUp() override {
     memory_ = new MemoryFake;
     process_memory_.reset(memory_);
   }
@@ -82,17 +80,13 @@
   MemoryFake* memory_;
   std::shared_ptr<Memory> process_memory_;
 
-  static TemporaryFile elf_;
+  TemporaryFile elf_;
 
-  static TemporaryFile elf_at_1000_;
+  TemporaryFile elf_at_1000_;
 
-  static TemporaryFile elf32_at_map_;
-  static TemporaryFile elf64_at_map_;
+  TemporaryFile elf32_at_map_;
+  TemporaryFile elf64_at_map_;
 };
-TemporaryFile MapInfoCreateMemoryTest::elf_;
-TemporaryFile MapInfoCreateMemoryTest::elf_at_1000_;
-TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
-TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
 
 TEST_F(MapInfoCreateMemoryTest, end_le_start) {
   MapInfo info(nullptr, 0x100, 0x100, 0, 0, elf_.path);
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index baada82..bded57a 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1457,4 +1457,81 @@
   EXPECT_EQ(0xc2044218, unwinder.frames()[0].sp);
 }
 
+TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
+  ASSERT_NO_FATAL_FAILURE(Init("load_bias_ro_rx_x86_64/", ARCH_X86_64));
+
+  Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+  unwinder.Unwind();
+
+  std::string frame_info(DumpFrames(unwinder));
+  ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+  EXPECT_EQ(
+      "  #00 pc 00000000000e9dd4  libc.so (__write+20)\n"
+      "  #01 pc 000000000007ab9c  libc.so (_IO_file_write+44)\n"
+      "  #02 pc 0000000000079f3e  libc.so\n"
+      "  #03 pc 000000000007bce8  libc.so (_IO_do_write+24)\n"
+      "  #04 pc 000000000007b26e  libc.so (_IO_file_xsputn+270)\n"
+      "  #05 pc 000000000004f7f9  libc.so (_IO_vfprintf+1945)\n"
+      "  #06 pc 0000000000057cb5  libc.so (_IO_printf+165)\n"
+      "  #07 pc 0000000000ed1796  perfetto_unittests "
+      "(testing::internal::PrettyUnitTestResultPrinter::OnTestIterationStart(testing::UnitTest "
+      "const&, int)+374)\n"
+      "  #08 pc 0000000000ed30fd  perfetto_unittests "
+      "(testing::internal::TestEventRepeater::OnTestIterationStart(testing::UnitTest const&, "
+      "int)+125)\n"
+      "  #09 pc 0000000000ed5e25  perfetto_unittests "
+      "(testing::internal::UnitTestImpl::RunAllTests()+581)\n"
+      "  #10 pc 0000000000ef63f3  perfetto_unittests "
+      "(bool "
+      "testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
+      "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
+      "const*)+131)\n"
+      "  #11 pc 0000000000ee2a21  perfetto_unittests "
+      "(bool "
+      "testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, "
+      "bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char "
+      "const*)+113)\n"
+      "  #12 pc 0000000000ed5bb9  perfetto_unittests (testing::UnitTest::Run()+185)\n"
+      "  #13 pc 0000000000e900f0  perfetto_unittests (RUN_ALL_TESTS()+16)\n"
+      "  #14 pc 0000000000e900d8  perfetto_unittests (main+56)\n"
+      "  #15 pc 000000000002352a  libc.so (__libc_start_main+234)\n"
+      "  #16 pc 0000000000919029  perfetto_unittests (_start+41)\n",
+      frame_info);
+
+  EXPECT_EQ(0x7f9326a57dd4ULL, unwinder.frames()[0].pc);
+  EXPECT_EQ(0x7ffd224153c8ULL, unwinder.frames()[0].sp);
+  EXPECT_EQ(0x7f93269e8b9cULL, unwinder.frames()[1].pc);
+  EXPECT_EQ(0x7ffd224153d0ULL, unwinder.frames()[1].sp);
+  EXPECT_EQ(0x7f93269e7f3eULL, unwinder.frames()[2].pc);
+  EXPECT_EQ(0x7ffd22415400ULL, unwinder.frames()[2].sp);
+  EXPECT_EQ(0x7f93269e9ce8ULL, unwinder.frames()[3].pc);
+  EXPECT_EQ(0x7ffd22415440ULL, unwinder.frames()[3].sp);
+  EXPECT_EQ(0x7f93269e926eULL, unwinder.frames()[4].pc);
+  EXPECT_EQ(0x7ffd22415450ULL, unwinder.frames()[4].sp);
+  EXPECT_EQ(0x7f93269bd7f9ULL, unwinder.frames()[5].pc);
+  EXPECT_EQ(0x7ffd22415490ULL, unwinder.frames()[5].sp);
+  EXPECT_EQ(0x7f93269c5cb5ULL, unwinder.frames()[6].pc);
+  EXPECT_EQ(0x7ffd22415a10ULL, unwinder.frames()[6].sp);
+  EXPECT_EQ(0xed1796ULL, unwinder.frames()[7].pc);
+  EXPECT_EQ(0x7ffd22415af0ULL, unwinder.frames()[7].sp);
+  EXPECT_EQ(0xed30fdULL, unwinder.frames()[8].pc);
+  EXPECT_EQ(0x7ffd22415b70ULL, unwinder.frames()[8].sp);
+  EXPECT_EQ(0xed5e25ULL, unwinder.frames()[9].pc);
+  EXPECT_EQ(0x7ffd22415bb0ULL, unwinder.frames()[9].sp);
+  EXPECT_EQ(0xef63f3ULL, unwinder.frames()[10].pc);
+  EXPECT_EQ(0x7ffd22415c60ULL, unwinder.frames()[10].sp);
+  EXPECT_EQ(0xee2a21ULL, unwinder.frames()[11].pc);
+  EXPECT_EQ(0x7ffd22415cc0ULL, unwinder.frames()[11].sp);
+  EXPECT_EQ(0xed5bb9ULL, unwinder.frames()[12].pc);
+  EXPECT_EQ(0x7ffd22415d40ULL, unwinder.frames()[12].sp);
+  EXPECT_EQ(0xe900f0ULL, unwinder.frames()[13].pc);
+  EXPECT_EQ(0x7ffd22415d90ULL, unwinder.frames()[13].sp);
+  EXPECT_EQ(0xe900d8ULL, unwinder.frames()[14].pc);
+  EXPECT_EQ(0x7ffd22415da0ULL, unwinder.frames()[14].sp);
+  EXPECT_EQ(0x7f932699152aULL, unwinder.frames()[15].pc);
+  EXPECT_EQ(0x7ffd22415dd0ULL, unwinder.frames()[15].sp);
+  EXPECT_EQ(0x919029ULL, unwinder.frames()[16].pc);
+  EXPECT_EQ(0x7ffd22415e90ULL, unwinder.frames()[16].sp);
+}
+
 }  // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
new file mode 100644
index 0000000..63383d0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
new file mode 100644
index 0000000..ba5a31b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
@@ -0,0 +1,3 @@
+200000-919000 r--p 0 00:00 0   perfetto_unittests
+919000-1a0c000 r-xp 719000 00:00 0   perfetto_unittests
+7f932696e000-7f9326b23000 r-xp 0 00:00 0   libc.so
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
new file mode 100644
index 0000000..a30e599
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
new file mode 100644
index 0000000..6cb4055
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
@@ -0,0 +1,17 @@
+rax: 3b
+rbx: 3b
+rcx: 7f9326a57dd4
+rdx: 3b
+r8: 7ffd22415b09
+r9: 7ffd224155e0
+r10: 0
+r11: 246
+r12: 7f9326d28760
+r13: 3b
+r14: 7f9326d23760
+r15: 3b
+rdi: 1
+rsi: 2678850
+rbp: 2678850
+rsp: 7ffd224153c8
+rip: 7f9326a57dd4
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
new file mode 100644
index 0000000..4edfe07
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
Binary files differ
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 0025c56..d13548e 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -468,21 +468,6 @@
     unlockBuffer(len);
 }
 
-size_t String8::getUtf32Length() const
-{
-    return utf8_to_utf32_length(mString, length());
-}
-
-int32_t String8::getUtf32At(size_t index, size_t *next_index) const
-{
-    return utf32_from_utf8_at(mString, length(), index, next_index);
-}
-
-void String8::getUtf32(char32_t* dst) const
-{
-    utf8_to_utf32(mString, length(), dst);
-}
-
 // ---------------------------------------------------------------------------
 // Path functions
 
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index 24a745a..b08e061 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -452,48 +452,6 @@
     *codePoint |= 0x3F & byte;
 }
 
-size_t utf8_to_utf32_length(const char *src, size_t src_len)
-{
-    if (src == nullptr || src_len == 0) {
-        return 0;
-    }
-    size_t ret = 0;
-    const char* cur;
-    const char* end;
-    size_t num_to_skip;
-    for (cur = src, end = src + src_len, num_to_skip = 1;
-         cur < end;
-         cur += num_to_skip, ret++) {
-        const char first_char = *cur;
-        num_to_skip = 1;
-        if ((first_char & 0x80) == 0) {  // ASCII
-            continue;
-        }
-        int32_t mask;
-
-        for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) {
-        }
-    }
-    return ret;
-}
-
-void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst)
-{
-    if (src == nullptr || src_len == 0 || dst == nullptr) {
-        return;
-    }
-
-    const char* cur = src;
-    const char* const end = src + src_len;
-    char32_t* cur_utf32 = dst;
-    while (cur < end) {
-        size_t num_read;
-        *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read));
-        cur += num_read;
-    }
-    *cur_utf32 = 0;
-}
-
 static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length)
 {
     uint32_t unicode;
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
index e488e60..b04e5c1 100644
--- a/libutils/include/utils/LightRefBase.h
+++ b/libutils/include/utils/LightRefBase.h
@@ -47,8 +47,6 @@
         return mCount.load(std::memory_order_relaxed);
     }
 
-    typedef LightRefBase<T> basetype;
-
 protected:
     inline ~LightRefBase() { }
 
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index a105474..42c6efb 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -188,9 +188,6 @@
 // ---------------------------------------------------------------------------
 namespace android {
 
-class TextOutput;
-TextOutput& printWeakPointer(TextOutput& to, const void* val);
-
 // ---------------------------------------------------------------------------
 
 #define COMPARE_WEAK(_op_)                                      \
@@ -299,8 +296,6 @@
         getWeakRefs()->trackMe(enable, retain); 
     }
 
-    typedef RefBase basetype;
-
 protected:
                             RefBase();
     virtual                 ~RefBase();
@@ -459,9 +454,6 @@
     weakref_type*   m_refs;
 };
 
-template <typename T>
-TextOutput& operator<<(TextOutput& to, const wp<T>& val);
-
 #undef COMPARE_WEAK
 
 // ---------------------------------------------------------------------------
@@ -635,12 +627,6 @@
     }
 }
 
-template <typename T>
-inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
-{
-    return printWeakPointer(to, val.unsafe_get());
-}
-
 // ---------------------------------------------------------------------------
 
 // this class just serves as a namespace so TYPE::moveReferences can stay
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index c8f584e..0ddcbb2 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -95,13 +95,6 @@
                     __attribute__((format (printf, 2, 3)));
             status_t            appendFormatV(const char* fmt, va_list args);
 
-            // Note that this function takes O(N) time to calculate the value.
-            // No cache value is stored.
-            size_t              getUtf32Length() const;
-            int32_t             getUtf32At(size_t index,
-                                           size_t *next_index) const;
-            void                getUtf32(char32_t* dst) const;
-
     inline  String8&            operator=(const String8& other);
     inline  String8&            operator=(const char* other);
 
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index a2aaa47..fc6712d 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -129,18 +129,6 @@
 ssize_t utf8_length(const char *src);
 
 /**
- * Measure the length of a UTF-32 string.
- */
-size_t utf8_to_utf32_length(const char *src, size_t src_len);
-
-/**
- * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large
- * enough to store the entire converted string as measured by
- * utf8_to_utf32_length plus space for a NUL terminator.
- */
-void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst);
-
-/**
  * Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case
  * it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you
  * can ask to log a message and fail in case the invalid utf8 could have caused an override if no
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index e5b536c..f4544a1 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -3,7 +3,7 @@
 cc_library {
     name: "libvndksupport",
     native_bridge_supported: true,
-    srcs: ["linker.c"],
+    srcs: ["linker.cpp"],
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
deleted file mode 100644
index 84c2132..0000000
--- a/libvndksupport/linker.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "linker.h"
-
-#include <android/dlext.h>
-#include <dlfcn.h>
-
-#define LOG_TAG "vndksupport"
-#include <log/log.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-__attribute__((weak)) extern struct android_namespace_t* android_get_exported_namespace(const char*);
-__attribute__((weak)) extern void* android_dlopen_ext(const char*, int, const android_dlextinfo*);
-
-static const char* namespace_name = NULL;
-
-static struct android_namespace_t* get_vendor_namespace() {
-    const char* namespace_names[] = {"sphal", "default", NULL};
-    static struct android_namespace_t* vendor_namespace = NULL;
-    if (vendor_namespace == NULL) {
-        int name_idx = 0;
-        while (namespace_names[name_idx] != NULL) {
-            if (android_get_exported_namespace != NULL) {
-                vendor_namespace = android_get_exported_namespace(namespace_names[name_idx]);
-            }
-            if (vendor_namespace != NULL) {
-                namespace_name = namespace_names[name_idx];
-                break;
-            }
-            name_idx++;
-        }
-    }
-    return vendor_namespace;
-}
-
-int android_is_in_vendor_process() {
-    // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
-    // loaded (sysprop service isn't up for init to know <ver>).
-    if (getpid() == 1) {
-        return 0;
-    }
-    if (android_get_exported_namespace == NULL) {
-        ALOGD("android_get_exported_namespace() not available. Assuming system process.");
-        return 0;
-    }
-
-    // In vendor process, 'vndk' namespace is not visible, whereas in system
-    // process, it is.
-    return android_get_exported_namespace("vndk") == NULL;
-}
-
-void* android_load_sphal_library(const char* name, int flag) {
-    struct android_namespace_t* vendor_namespace = get_vendor_namespace();
-    if (vendor_namespace != NULL) {
-        const android_dlextinfo dlextinfo = {
-            .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = vendor_namespace,
-        };
-        void* handle = NULL;
-        if (android_dlopen_ext != NULL) {
-            handle = android_dlopen_ext(name, flag, &dlextinfo);
-        }
-        if (!handle) {
-            ALOGE("Could not load %s from %s namespace: %s.", name, namespace_name, dlerror());
-        }
-        return handle;
-    } else {
-        ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
-        return dlopen(name, flag);
-    }
-}
-
-int android_unload_sphal_library(void* handle) {
-    return dlclose(handle);
-}
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
new file mode 100644
index 0000000..cf0f618
--- /dev/null
+++ b/libvndksupport/linker.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "vndksupport"
+
+#include "linker.h"
+
+#include <android/dlext.h>
+#include <dlfcn.h>
+#include <log/log.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <initializer_list>
+
+__attribute__((weak)) extern "C" android_namespace_t* android_get_exported_namespace(const char*);
+__attribute__((weak)) extern "C" void* android_dlopen_ext(const char*, int,
+                                                          const android_dlextinfo*);
+
+namespace {
+
+struct VendorNamespace {
+    android_namespace_t* ptr = nullptr;
+    const char* name = nullptr;
+};
+
+}  // anonymous namespace
+
+static VendorNamespace get_vendor_namespace() {
+    static VendorNamespace result = ([] {
+        for (const char* name : {"sphal", "default"}) {
+            if (android_get_exported_namespace != nullptr) {
+                if (android_namespace_t* ns = android_get_exported_namespace(name)) {
+                    return VendorNamespace{ns, name};
+                }
+            }
+        }
+        return VendorNamespace{};
+    })();
+    return result;
+}
+
+int android_is_in_vendor_process() {
+    // Special case init, since when init runs, ld.config.<ver>.txt hasn't been
+    // loaded (sysprop service isn't up for init to know <ver>).
+    if (getpid() == 1) {
+        return 0;
+    }
+    if (android_get_exported_namespace == nullptr) {
+        ALOGD("android_get_exported_namespace() not available. Assuming system process.");
+        return 0;
+    }
+
+    // In vendor process, 'vndk' namespace is not visible, whereas in system
+    // process, it is.
+    return android_get_exported_namespace("vndk") == nullptr;
+}
+
+void* android_load_sphal_library(const char* name, int flag) {
+    VendorNamespace vendor_namespace = get_vendor_namespace();
+    if (vendor_namespace.ptr != nullptr) {
+        const android_dlextinfo dlextinfo = {
+                .flags = ANDROID_DLEXT_USE_NAMESPACE,
+                .library_namespace = vendor_namespace.ptr,
+        };
+        void* handle = nullptr;
+        if (android_dlopen_ext != nullptr) {
+            handle = android_dlopen_ext(name, flag, &dlextinfo);
+        }
+        if (!handle) {
+            ALOGE("Could not load %s from %s namespace: %s.", name, vendor_namespace.name,
+                  dlerror());
+        }
+        return handle;
+    } else {
+        ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
+        return dlopen(name, flag);
+    }
+}
+
+int android_unload_sphal_library(void* handle) {
+    return dlclose(handle);
+}
diff --git a/libvndksupport/tests/linker_test.cpp b/libvndksupport/tests/linker_test.cpp
index 7ce27d4..d0c8ef7 100644
--- a/libvndksupport/tests/linker_test.cpp
+++ b/libvndksupport/tests/linker_test.cpp
@@ -21,11 +21,6 @@
 #include <vndksupport/linker.h>
 #include <string>
 
-// Since the test executable will be in /data and ld.config.txt does not
-// configure sphal namespace for executables in /data, the call to
-// android_load_sphal_library will always fallback to the plain dlopen from the
-// default namespace.
-
 // Let's use libEGL_<chipset>.so as a SP-HAL in test
 static std::string find_sphal_lib() {
     const char* path =
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 3843252..0253f2f 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -76,6 +76,10 @@
         "liblog",
     ],
 
+    // for FRIEND_TEST
+    static_libs: ["libgtest_prod"],
+    export_static_lib_headers: ["libgtest_prod"],
+
     export_include_dirs: ["include"],
 }
 
diff --git a/libziparchive/include/ziparchive/zip_writer.h b/libziparchive/include/ziparchive/zip_writer.h
index a2a0dbf..d68683d 100644
--- a/libziparchive/include/ziparchive/zip_writer.h
+++ b/libziparchive/include/ziparchive/zip_writer.h
@@ -19,6 +19,7 @@
 #include <cstdio>
 #include <ctime>
 
+#include <gtest/gtest_prod.h>
 #include <memory>
 #include <string>
 #include <string_view>
@@ -165,6 +166,7 @@
   int32_t StoreBytes(FileEntry* file, const void* data, uint32_t len);
   int32_t CompressBytes(FileEntry* file, const void* data, uint32_t len);
   int32_t FlushCompressedBytes(FileEntry* file);
+  bool ShouldUseDataDescriptor() const;
 
   enum class State {
     kWritingZip,
@@ -182,4 +184,6 @@
 
   std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_;
   std::vector<uint8_t> buffer_;
+
+  FRIEND_TEST(zipwriter, WriteToUnseekableFile);
 };
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 198154b..67279a6 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -455,6 +455,11 @@
   return kNoError;
 }
 
+bool ZipWriter::ShouldUseDataDescriptor() const {
+  // Only use a trailing "data descriptor" if the output isn't seekable.
+  return !seekable_;
+}
+
 int32_t ZipWriter::FinishEntry() {
   if (state_ != State::kWritingEntry) {
     return kInvalidState;
@@ -467,7 +472,7 @@
     }
   }
 
-  if ((current_file_entry_.compression_method & kCompressDeflated) || !seekable_) {
+  if (ShouldUseDataDescriptor()) {
     // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
     // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
     const uint32_t sig = DataDescriptor::kOptSignature;
@@ -515,7 +520,7 @@
   for (FileEntry& file : files_) {
     CentralDirectoryRecord cdr = {};
     cdr.record_signature = CentralDirectoryRecord::kSignature;
-    if ((file.compression_method & kCompressDeflated) || !seekable_) {
+    if (ShouldUseDataDescriptor()) {
       cdr.gpb_flags |= kGPBDDFlagMask;
     }
     cdr.compression_method = file.compression_method;
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index c3da23c..d324d4b 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -243,6 +243,7 @@
   ZipEntry data;
   ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
   EXPECT_EQ(kCompressDeflated, data.method);
+  EXPECT_EQ(0u, data.has_data_descriptor);
   ASSERT_EQ(4u, data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
 
@@ -351,6 +352,29 @@
   CloseArchive(handle);
 }
 
+TEST_F(zipwriter, WriteToUnseekableFile) {
+  const char* expected = "hello";
+  ZipWriter writer(file_);
+  writer.seekable_ = false;
+
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes(expected, strlen(expected)));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, "file.txt", &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(1u, data.has_data_descriptor);
+  EXPECT_EQ(strlen(expected), data.compressed_length);
+  ASSERT_EQ(strlen(expected), data.uncompressed_length);
+  ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
+  CloseArchive(handle);
+}
+
 TEST_F(zipwriter, TruncateFileAfterBackup) {
   ZipWriter writer(file_);
 
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 48140b8..42af751 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -79,6 +79,7 @@
 #define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
 #define ZONEINFO_PATH "/proc/zoneinfo"
 #define MEMINFO_PATH "/proc/meminfo"
+#define PROC_STATUS_TGID_FIELD "Tgid:"
 #define LINE_MAX 128
 
 /* Android Logger event logtags (see event.logtags) */
@@ -551,6 +552,49 @@
            (to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS;
 }
 
+static int proc_get_tgid(int pid) {
+    char path[PATH_MAX];
+    char buf[PAGE_SIZE];
+    int fd;
+    ssize_t size;
+    char *pos;
+    int64_t tgid = -1;
+
+    snprintf(path, PATH_MAX, "/proc/%d/status", pid);
+    fd = open(path, O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        return -1;
+    }
+
+    size = read_all(fd, buf, sizeof(buf) - 1);
+    if (size < 0) {
+        goto out;
+    }
+    buf[size] = 0;
+
+    pos = buf;
+    while (true) {
+        pos = strstr(pos, PROC_STATUS_TGID_FIELD);
+        /* Stop if TGID tag not found or found at the line beginning */
+        if (pos == NULL || pos == buf || pos[-1] == '\n') {
+            break;
+        }
+        pos++;
+    }
+
+    if (pos == NULL) {
+        goto out;
+    }
+
+    pos += strlen(PROC_STATUS_TGID_FIELD);
+    while (*pos == ' ') pos++;
+    parse_int64(pos, &tgid);
+
+out:
+    close(fd);
+    return (int)tgid;
+}
+
 static void cmd_procprio(LMKD_CTRL_PACKET packet) {
     struct proc *procp;
     char path[80];
@@ -559,6 +603,7 @@
     struct lmk_procprio params;
     bool is_system_server;
     struct passwd *pwdrec;
+    int tgid;
 
     lmkd_pack_get_procprio(packet, &params);
 
@@ -568,6 +613,14 @@
         return;
     }
 
+    /* Check if registered process is a thread group leader */
+    tgid = proc_get_tgid(params.pid);
+    if (tgid >= 0 && tgid != params.pid) {
+        ALOGE("Attempt to register a task that is not a thread group leader (tid %d, tgid %d)",
+            params.pid, tgid);
+        return;
+    }
+
     /* gid containing AID_READPROC required */
     /* CAP_SYS_RESOURCE required */
     /* CAP_DAC_OVERRIDE required */
@@ -1332,6 +1385,7 @@
 static int kill_one_process(struct proc* procp, int min_oom_score) {
     int pid = procp->pid;
     uid_t uid = procp->uid;
+    int tgid;
     char *taskname;
     int tasksize;
     int r;
@@ -1345,6 +1399,12 @@
     (void)(min_oom_score);
 #endif
 
+    tgid = proc_get_tgid(pid);
+    if (tgid >= 0 && tgid != pid) {
+        ALOGE("Possible pid reuse detected (pid %d, tgid %d)!", pid, tgid);
+        goto out;
+    }
+
     taskname = proc_get_name(pid);
     if (!taskname) {
         goto out;
diff --git a/logcat/Android.bp b/logcat/Android.bp
index 5030b15..f1b18b2 100644
--- a/logcat/Android.bp
+++ b/logcat/Android.bp
@@ -24,7 +24,6 @@
     ],
     shared_libs: [
         "libbase",
-        "libpcrecpp",
         "libprocessgroup",
     ],
     static_libs: ["liblog"],
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 15e07fe..e2711af 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -41,6 +41,7 @@
 
 #include <atomic>
 #include <memory>
+#include <regex>
 #include <string>
 #include <utility>
 #include <vector>
@@ -56,8 +57,6 @@
 #include <processgroup/sched_policy.h>
 #include <system/thread_defs.h>
 
-#include <pcrecpp.h>
-
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
 struct log_device_t {
@@ -113,7 +112,7 @@
     size_t outByteCount;
     int printBinary;
     int devCount;  // >1 means multiple
-    pcrecpp::RE* regex;
+    std::unique_ptr<std::regex> regex;
     log_device_t* devices;
     EventTagMap* eventTagMap;
     // 0 means "infinite"
@@ -307,9 +306,7 @@
                     const AndroidLogEntry& entry) {
     if (!context->regex) return true;
 
-    std::string messageString(entry.message, entry.messageLen);
-
-    return context->regex->PartialMatch(messageString);
+    return std::regex_search(entry.message, entry.message + entry.messageLen, *context->regex);
 }
 
 static void processBuffer(android_logcat_context_internal* context,
@@ -460,7 +457,7 @@
                     "  -d              Dump the log and then exit (don't block)\n"
                     "  -e <expr>, --regex=<expr>\n"
                     "                  Only print lines where the log message matches <expr>\n"
-                    "                  where <expr> is a Perl-compatible regular expression\n"
+                    "                  where <expr> is a regular expression\n"
                     // Leave --head undocumented as alias for -m
                     "  -m <count>, --max-count=<count>\n"
                     "                  Quit after printing <count> lines. This is meant to be\n"
@@ -483,7 +480,8 @@
                     "                  Additionally, 'kernel' for userdebug and eng builds, and\n"
                     "                  'security' for Device Owner installations.\n"
                     "                  Multiple -b parameters or comma separated list of buffers are\n"
-                    "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
+                    "                  allowed. Buffers interleaved.\n"
+                    "                  Default -b main,system,crash,kernel.\n"
                     "  -B, --binary    Output the log in binary.\n"
                     "  -S, --statistics                       Output statistics.\n"
                     "  -p, --prune     Print prune white and ~black list. Service is specified as\n"
@@ -898,6 +896,11 @@
             case 0:
                 // only long options
                 if (long_options[option_index].name == pid_str) {
+                    if (pid != 0) {
+                        logcat_panic(context, HELP_TRUE, "Only supports one PID argument.\n");
+                        goto exit;
+                    }
+
                     // ToDo: determine runtime PID_MAX?
                     if (!getSizeTArg(optarg, &pid, 1)) {
                         logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
@@ -994,7 +997,7 @@
                 break;
 
             case 'e':
-                context->regex = new pcrecpp::RE(optarg);
+                context->regex.reset(new std::regex(optarg));
                 break;
 
             case 'm': {
@@ -1312,6 +1315,10 @@
             dev = dev->next = new log_device_t("crash", false);
             context->devCount++;
         }
+        if (android_name_to_log_id("kernel") == LOG_ID_KERNEL) {
+            dev = dev->next = new log_device_t("kernel", false);
+            context->devCount++;
+        }
     }
 
     if (!!context->logRotateSizeKBytes && !context->outputFileName) {
@@ -1691,7 +1698,6 @@
         sched_yield();
     }
 
-    delete context->regex;
     context->argv_hold.clear();
     context->args.clear();
     context->envp_hold.clear();
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 25104eb..26c9de3 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -13,28 +13,13 @@
     # expect /init to report failure if property empty (default)
     setprop persist.logd.logpersistd.size ${persist.logd.logpersistd.count}
 
-on property:persist.logd.logpersistd.size=256
-    setprop persist.logd.logpersistd.size ""
-    setprop logd.logpersistd.size ""
-
 on property:persist.logd.logpersistd.size=*
-    # expect /init to report failure if property empty (default)
     setprop logd.logpersistd.size ${persist.logd.logpersistd.size}
 
-on property:persist.logd.logpersistd.rotate_kbytes=1024
-    setprop persist.logd.logpersistd.rotate_kbytes ""
-    setprop logd.logpersistd.rotate_kbytes ""
-
 on property:persist.logd.logpersistd.rotate_kbytes=*
-   # expect /init to report failure if property empty (default)
-   setprop logd.logpersistd.rotate_kbytes ${persist.logd.logpersistd.rotate_kbytes}
-
-on property:persist.logd.logpersistd.buffer=all
-    setprop persist.logd.logpersistd.buffer ""
-    setprop logd.logpersistd.buffer ""
+    setprop logd.logpersistd.rotate_kbytes ${persist.logd.logpersistd.rotate_kbytes}
 
 on property:persist.logd.logpersistd.buffer=*
-    # expect /init to report failure if property empty (default)
     setprop logd.logpersistd.buffer ${persist.logd.logpersistd.buffer}
 
 on property:persist.logd.logpersistd=logcatd
diff --git a/logd/Android.bp b/logd/Android.bp
index 9b86258..b337b7c 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -39,7 +39,6 @@
         "FlushCommand.cpp",
         "LogBuffer.cpp",
         "LogBufferElement.cpp",
-        "LogBufferInterface.cpp",
         "LogTimes.cpp",
         "LogStatistics.cpp",
         "LogWhiteBlackList.cpp",
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 774d4ab..c2d5b97 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,7 +27,6 @@
 #include <sysutils/SocketClient.h>
 
 #include "LogBufferElement.h"
-#include "LogBufferInterface.h"
 #include "LogStatistics.h"
 #include "LogTags.h"
 #include "LogTimes.h"
@@ -75,7 +74,7 @@
 
 typedef std::list<LogBufferElement*> LogBufferElementCollection;
 
-class LogBuffer : public LogBufferInterface {
+class LogBuffer {
     LogBufferElementCollection mLogElements;
     pthread_rwlock_t mLogElementsLock;
 
@@ -108,14 +107,14 @@
     LastLogTimes& mTimes;
 
     explicit LogBuffer(LastLogTimes* times);
-    ~LogBuffer() override;
+    ~LogBuffer();
     void init();
     bool isMonotonic() {
         return monotonic;
     }
 
-    int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
-            const char* msg, uint16_t len) override;
+    int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char* msg,
+            uint16_t len);
     // lastTid is an optional context to help detect if the last previous
     // valid message was from the same source so we can differentiate chatty
     // filter types (identical or expired)
@@ -159,12 +158,7 @@
     const char* pidToName(pid_t pid) {
         return stats.pidToName(pid);
     }
-    virtual uid_t pidToUid(pid_t pid) override {
-        return stats.pidToUid(pid);
-    }
-    virtual pid_t tidToPid(pid_t tid) override {
-        return stats.tidToPid(tid);
-    }
+    uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
     const char* uidToName(uid_t uid) {
         return stats.uidToName(uid);
     }
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 2fd9f95..82c4fb9 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -56,14 +56,7 @@
       mLogId(elem.mLogId),
       mDropped(elem.mDropped) {
     if (mDropped) {
-        if (elem.isBinary() && elem.mMsg != nullptr) {
-            // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
-            const int len = sizeof(android_event_header_t);
-            mMsg = new char[len];
-            memcpy(mMsg, elem.mMsg, len);
-        } else {
-            mMsg = nullptr;
-        }
+        mTag = elem.getTag();
     } else {
         mMsg = new char[mMsgLen];
         memcpy(mMsg, elem.mMsg, mMsgLen);
@@ -71,31 +64,43 @@
 }
 
 LogBufferElement::~LogBufferElement() {
-    delete[] mMsg;
+    if (!mDropped) {
+        delete[] mMsg;
+    }
 }
 
 uint32_t LogBufferElement::getTag() const {
-    return (isBinary() &&
-            ((mDropped && mMsg != nullptr) ||
-             (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
-               ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
-               : 0;
+    // Binary buffers have no tag.
+    if (!isBinary()) {
+        return 0;
+    }
+
+    // Dropped messages store the tag in place of mMsg.
+    if (mDropped) {
+        return mTag;
+    }
+
+    // For non-dropped messages, we get the tag from the message header itself.
+    if (mMsgLen < sizeof(android_event_header_t)) {
+        return 0;
+    }
+
+    return reinterpret_cast<const android_event_header_t*>(mMsg)->tag;
 }
 
 uint16_t LogBufferElement::setDropped(uint16_t value) {
-    // The tag information is saved in mMsg data, if the tag is non-zero
-    // save only the information needed to get the tag.
-    if (getTag() != 0) {
-        if (mMsgLen > sizeof(android_event_header_t)) {
-            char* truncated_msg = new char[sizeof(android_event_header_t)];
-            memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
-            delete[] mMsg;
-            mMsg = truncated_msg;
-        }  // mMsgLen == sizeof(android_event_header_t), already at minimum.
-    } else {
-        delete[] mMsg;
-        mMsg = nullptr;
+    if (mDropped) {
+        return mDroppedCount = value;
     }
+
+    // The tag information is saved in mMsg data, which is in a union with mTag, used after mDropped
+    // is set to true. Therefore we save the tag value aside, delete mMsg, then set mTag to the tag
+    // value in its place.
+    auto old_tag = getTag();
+    delete[] mMsg;
+    mMsg = nullptr;
+
+    mTag = old_tag;
     mDropped = true;
     return mDroppedCount = value;
 }
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 57b0a95..da4991b 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -41,7 +41,10 @@
     const uint32_t mPid;
     const uint32_t mTid;
     log_time mRealTime;
-    char* mMsg;
+    union {
+        char* mMsg;    // mDropped == false
+        int32_t mTag;  // mDropped == true
+    };
     union {
         const uint16_t mMsgLen;  // mDropped == false
         uint16_t mDroppedCount;  // mDropped == true
diff --git a/logd/LogBufferInterface.h b/logd/LogBufferInterface.h
deleted file mode 100644
index f31e244..0000000
--- a/logd/LogBufferInterface.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LOGD_LOG_BUFFER_INTERFACE_H__
-#define _LOGD_LOG_BUFFER_INTERFACE_H__
-
-#include <sys/types.h>
-
-#include <android-base/macros.h>
-#include <log/log_id.h>
-#include <log/log_time.h>
-
-// Abstract interface that handles log when log available.
-class LogBufferInterface {
-   public:
-    LogBufferInterface();
-    virtual ~LogBufferInterface();
-    // Handles a log entry when available in LogListener.
-    // Returns the size of the handled log message.
-    virtual int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
-                    pid_t tid, const char* msg, uint16_t len) = 0;
-
-    virtual uid_t pidToUid(pid_t pid);
-    virtual pid_t tidToPid(pid_t tid);
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(LogBufferInterface);
-};
-
-#endif  // _LOGD_LOG_BUFFER_INTERFACE_H__
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 2f22778..443570f 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#include <ctype.h>
 #include <limits.h>
-#include <stdio.h>
 #include <sys/cdefs.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
@@ -32,9 +30,8 @@
 #include "LogListener.h"
 #include "LogUtils.h"
 
-LogListener::LogListener(LogBufferInterface* buf, LogReader* reader)
-    : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {
-}
+LogListener::LogListener(LogBuffer* buf, LogReader* reader)
+    : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
 
 bool LogListener::onDataAvailable(SocketClient* cli) {
     static bool name_set;
@@ -78,11 +75,8 @@
         cmsg = CMSG_NXTHDR(&hdr, cmsg);
     }
 
-    struct ucred fake_cred;
     if (cred == nullptr) {
-        cred = &fake_cred;
-        cred->pid = 0;
-        cred->uid = DEFAULT_OVERFLOWUID;
+        return false;
     }
 
     if (cred->uid == AID_LOGD) {
@@ -106,40 +100,16 @@
         return false;
     }
 
-    // Check credential validity, acquire corrected details if not supplied.
-    if (cred->pid == 0) {
-        cred->pid = logbuf ? logbuf->tidToPid(header->tid)
-                           : android::tidToPid(header->tid);
-        if (cred->pid == getpid()) {
-            // We expect that /proc/<tid>/ is accessible to self even without
-            // readproc group, so that we will always drop messages that come
-            // from any of our logd threads and their library calls.
-            return false;  // ignore self
-        }
-    }
-    if (cred->uid == DEFAULT_OVERFLOWUID) {
-        uid_t uid =
-            logbuf ? logbuf->pidToUid(cred->pid) : android::pidToUid(cred->pid);
-        if (uid == AID_LOGD) {
-            uid = logbuf ? logbuf->pidToUid(header->tid)
-                         : android::pidToUid(cred->pid);
-        }
-        if (uid != AID_LOGD) cred->uid = uid;
-    }
-
     char* msg = ((char*)buffer) + sizeof(android_log_header_t);
     n -= sizeof(android_log_header_t);
 
     // NB: hdr.msg_flags & MSG_TRUNC is not tested, silently passing a
     // truncated message to the logs.
 
-    if (logbuf != nullptr) {
-        int res = logbuf->log(
-            logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
-            ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
-        if (res > 0 && reader != nullptr) {
-            reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
-        }
+    int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg,
+                          ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX);
+    if (res > 0) {
+        reader->notifyNewLog(static_cast<log_mask_t>(1 << logId));
     }
 
     return true;
diff --git a/logd/LogListener.h b/logd/LogListener.h
index a562a54..8fe3da4 100644
--- a/logd/LogListener.h
+++ b/logd/LogListener.h
@@ -20,22 +20,12 @@
 #include <sysutils/SocketListener.h>
 #include "LogReader.h"
 
-// DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of
-// the uapi headers for userspace to use.  This value is filled in on the
-// out-of-band socket credentials if the OS fails to find one available.
-// One of the causes of this is if SO_PASSCRED is set, all the packets before
-// that point will have this value.  We also use it in a fake credential if
-// no socket credentials are supplied.
-#ifndef DEFAULT_OVERFLOWUID
-#define DEFAULT_OVERFLOWUID 65534
-#endif
-
 class LogListener : public SocketListener {
-    LogBufferInterface* logbuf;
+    LogBuffer* logbuf;
     LogReader* reader;
 
    public:
-    LogListener(LogBufferInterface* buf, LogReader* reader /* nullable */);
+     LogListener(LogBuffer* buf, LogReader* reader);
 
    protected:
     virtual bool onDataAvailable(SocketClient* cli);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 116e08e..431b778 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -837,35 +837,12 @@
     }
     return AID_LOGD;  // associate this with the logger
 }
-
-pid_t tidToPid(pid_t tid) {
-    char buffer[512];
-    snprintf(buffer, sizeof(buffer), "/proc/%u/status", tid);
-    FILE* fp = fopen(buffer, "r");
-    if (fp) {
-        while (fgets(buffer, sizeof(buffer), fp)) {
-            int pid = tid;
-            char space = 0;
-            if ((sscanf(buffer, "Tgid: %d%c", &pid, &space) == 2) &&
-                isspace(space)) {
-                fclose(fp);
-                return pid;
-            }
-        }
-        fclose(fp);
-    }
-    return tid;
-}
 }
 
 uid_t LogStatistics::pidToUid(pid_t pid) {
     return pidTable.add(pid)->second.getUid();
 }
 
-pid_t LogStatistics::tidToPid(pid_t tid) {
-    return tidTable.add(tid)->second.getPid();
-}
-
 // caller must free character string
 const char* LogStatistics::pidToName(pid_t pid) const {
     // An inconvenient truth ... getName() can alter the object
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 469f6dc..0782de3 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -306,6 +306,10 @@
     std::string format(const LogStatistics& stat, log_id_t id) const;
 };
 
+namespace android {
+uid_t pidToUid(pid_t pid);
+}
+
 struct PidEntry : public EntryBaseDropped {
     const pid_t pid;
     uid_t uid;
@@ -385,13 +389,6 @@
           uid(android::pidToUid(tid)),
           name(android::tidToName(tid)) {
     }
-    TidEntry(pid_t tid)
-        : EntryBaseDropped(),
-          tid(tid),
-          pid(android::tidToPid(tid)),
-          uid(android::pidToUid(tid)),
-          name(android::tidToName(tid)) {
-    }
     explicit TidEntry(const LogBufferElement* element)
         : EntryBaseDropped(element),
           tid(element->getTid()),
@@ -787,7 +784,6 @@
     // helper (must be locked directly or implicitly by mLogElementsLock)
     const char* pidToName(pid_t pid) const;
     uid_t pidToUid(pid_t pid);
-    pid_t tidToPid(pid_t tid);
     const char* uidToName(uid_t uid) const;
 };
 
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 4dcd3e7..fa9f398 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -38,8 +38,6 @@
 // Caller must own and free returned value
 char* pidToName(pid_t pid);
 char* tidToName(pid_t tid);
-uid_t pidToUid(pid_t pid);
-pid_t tidToPid(pid_t tid);
 
 // Furnished in LogTags.cpp. Thread safe.
 const char* tagToName(uint32_t tag);
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index f084cd2..4559050 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -113,10 +113,10 @@
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
 endif
-ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
-  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product_services
+ifdef BOARD_USES_SYSTEM_EXTIMAGE
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_ext
 else
-  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product_services $(TARGET_ROOT_OUT)/product_services
+  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/system_ext $(TARGET_ROOT_OUT)/system_ext
 endif
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
@@ -230,7 +230,7 @@
 # A symlink can't overwrite a directory and the /system/usr/icu directory once
 # existed so the required structure must be created whatever we find.
 LOCAL_POST_INSTALL_CMD = mkdir -p $(TARGET_OUT)/usr && rm -rf $(TARGET_OUT)/usr/icu
-LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.runtime/etc/icu $(TARGET_OUT)/usr/icu
+LOCAL_POST_INSTALL_CMD += && ln -sf /apex/com.android.i18n/etc/icu $(TARGET_OUT)/usr/icu
 
 # TODO(b/124106384): Clean up compat symlinks for ART binaries.
 ART_BINARIES := \
@@ -378,6 +378,62 @@
 		echo $(lib).so >> $@;)
 
 #######################################
+# vndkcore.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndkcore.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_LIBRARIES := $(VNDK_CORE_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_VNDK_CORE_LIBRARIES), \
+		echo $(lib).so >> $@;)
+
+#######################################
+# vndkprivate.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndkprivate.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_PRIVATE_LIBRARIES := $(VNDK_PRIVATE_LIBRARIES)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_VNDK_PRIVATE_LIBRARIES), \
+		echo $(lib).so >> $@;)
+
+#######################################
+# sanitizer.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := sanitizer.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(addsuffix .so,\
+  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(UBSAN_RUNTIME_LIBRARY) \
+  $(TSAN_RUNTIME_LIBRARY) \
+  $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_UBSAN_RUNTIME_LIBRARY) \
+  $(2ND_TSAN_RUNTIME_LIBRARY))
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES), \
+		echo $(lib) >> $@;)
+
+#######################################
 # adb_debug.prop in debug ramdisk
 include $(CLEAR_VARS)
 LOCAL_MODULE := adb_debug.prop
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index ad14493..f0b1fd2 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -42,7 +42,7 @@
 # APEX related namespaces.
 ###############################################################################
 
-additional.namespaces = runtime,conscrypt,media,resolv
+additional.namespaces = runtime,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
@@ -51,8 +51,8 @@
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
-namespace.default.links = runtime,resolv
-namespace.default.asan.links = runtime,resolv
+namespace.default.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
@@ -71,6 +71,9 @@
 # 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
 #
@@ -159,6 +162,27 @@
 namespace.resolv.link.default.shared_libs += libvndksupport.so
 
 ###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs  = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
+
+
+###############################################################################
 # Namespace config for binaries under /postinstall.
 # Only one default namespace is defined and it has no directories other than
 # /system/lib and /product/lib in the search paths. This is because linker
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index b1616d3..a639592 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,6 +7,7 @@
 # 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/
@@ -34,7 +35,7 @@
 dir.system = /data
 
 [system]
-additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -48,8 +49,8 @@
 namespace.default.visible = true
 
 namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 # We can't have entire /system/${LIB} as permitted paths because doing so
 # makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -61,12 +62,15 @@
 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}
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/${LIB}
 # These are where odex files are located. libart has to be able to dlopen the files
 namespace.default.permitted.paths += /system/framework
 namespace.default.permitted.paths += /system/app
 namespace.default.permitted.paths += /system/priv-app
+namespace.default.permitted.paths += /%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
@@ -80,9 +84,6 @@
 namespace.default.permitted.paths += /%PRODUCT%/framework
 namespace.default.permitted.paths += /%PRODUCT%/app
 namespace.default.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/framework
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/app
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.permitted.paths += /data
 namespace.default.permitted.paths += /mnt/expand
 namespace.default.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
@@ -90,10 +91,10 @@
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths +=           /%SYSTEM_EXT%/${LIB}
 namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.permitted.paths  = /data
 namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -102,6 +103,10 @@
 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
@@ -116,10 +121,6 @@
 namespace.default.asan.permitted.paths += /%PRODUCT%/framework
 namespace.default.asan.permitted.paths += /%PRODUCT%/app
 namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/framework
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
 namespace.default.asan.permitted.paths += /mnt/expand
 namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
 namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
@@ -131,7 +132,7 @@
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
-namespace.default.links = runtime,resolv
+namespace.default.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
@@ -151,6 +152,9 @@
 # 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
 #
@@ -184,13 +188,16 @@
 
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
-namespace.media.links = default
+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
 #
@@ -268,7 +275,7 @@
 # libs listed here can be used. Order is important here as the namespaces are
 # tried in this order. rs should be before vndk because both are capable
 # of loading libRS_internal.so
-namespace.sphal.links = rs,default,vndk
+namespace.sphal.links = rs,default,vndk,neuralnetworks
 
 # Renderscript gets separate namespace
 namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -278,6 +285,10 @@
 
 namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
+# LLNDK library moved into apex
+namespace.sphal.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+
 ###############################################################################
 # "rs" namespace
 #
@@ -317,7 +328,7 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,vndk
+namespace.rs.links = default,vndk,neuralnetworks
 
 namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -327,6 +338,10 @@
 
 namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
+# LLNDK library moved into apex
+namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+
 ###############################################################################
 # "vndk" namespace
 #
@@ -372,7 +387,7 @@
 # The "vndk" namespace links to "default" namespace for LLNDK libs and links to
 # "sphal" namespace for vendor libs.  The ordering matters.  The "default"
 # namespace has higher priority than the "sphal" namespace.
-namespace.vndk.links = default,sphal,runtime
+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
@@ -385,6 +400,28 @@
 # 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
@@ -393,7 +430,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = runtime,system,vndk%VNDK_IN_SYSTEM_NS%
+additional.namespaces = runtime,system,neuralnetworks,vndk%VNDK_IN_SYSTEM_NS%
 
 ###############################################################################
 # "default" namespace
@@ -434,7 +471,7 @@
 namespace.default.asan.permitted.paths += /data/asan/vendor
 namespace.default.asan.permitted.paths +=           /vendor
 
-namespace.default.links = system,vndk%VNDK_IN_SYSTEM_NS%,runtime
+namespace.default.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%
@@ -442,6 +479,9 @@
 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
 #
@@ -457,6 +497,7 @@
 # classloader-namespace for oat files, and tighten this up.
 namespace.runtime.link.system.allow_all_shared_libs = true
 
+
 ###############################################################################
 # "vndk" namespace
 #
@@ -490,7 +531,7 @@
 # Android releases.  The links here should be identical to that of the
 # 'vndk_in_system' namespace, except for the link between 'vndk' and
 # 'vndk_in_system'.
-namespace.vndk.links = system,default%VNDK_IN_SYSTEM_NS%,runtime
+namespace.vndk.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%
@@ -501,6 +542,9 @@
 
 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
 #
@@ -510,15 +554,15 @@
 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.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.system.asan.search.paths +=           /system/${LIB}
-namespace.system.asan.search.paths += /data/asan/product/${LIB}
+namespace.system.asan.search.paths += /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.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 namespace.system.links = runtime
 namespace.system.link.runtime.shared_libs  = libdexfile_external.so
@@ -553,15 +597,15 @@
 # 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.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.vndk_in_system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.vndk_in_system.asan.search.paths +=           /system/${LIB}
-namespace.vndk_in_system.asan.search.paths += /data/asan/product/${LIB}
+namespace.vndk_in_system.asan.search.paths += /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.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.vndk_in_system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 namespace.vndk_in_system.whitelisted = %VNDK_USING_CORE_VARIANT_LIBRARIES%
 
@@ -570,14 +614,34 @@
 #   1. 'vndk_in_system' needs to be freely linked back to 'vndk'.
 #   2. 'vndk_in_system' does not need to link to 'default', as any library that
 #      requires anything vendor would not be a vndk_in_system library.
-namespace.vndk_in_system.links = vndk,system,runtime
+namespace.vndk_in_system.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
@@ -586,7 +650,7 @@
 # includes the requisite namespace setup for APEXes.
 ###############################################################################
 [unrestricted]
-additional.namespaces = runtime,media,conscrypt,resolv
+additional.namespaces = runtime,media,conscrypt,resolv,neuralnetworks
 
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
@@ -604,7 +668,7 @@
 namespace.default.asan.search.paths +=           /vendor/${LIB}
 
 # Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-namespace.default.links = runtime,resolv
+namespace.default.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
@@ -620,6 +684,7 @@
 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
@@ -652,12 +717,16 @@
 
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
-namespace.media.links = default
+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
 #
@@ -693,6 +762,25 @@
 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.
@@ -705,7 +793,5 @@
 [postinstall]
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
-
-namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 9212408..0bb60ab 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,6 +7,7 @@
 # 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/
@@ -34,7 +35,7 @@
 dir.system = /data
 
 [system]
-additional.namespaces = runtime,conscrypt,media,resolv,sphal,vndk,rs
+additional.namespaces = runtime,conscrypt,media,neuralnetworks,resolv,sphal,vndk,rs
 
 ###############################################################################
 # "default" namespace
@@ -48,21 +49,21 @@
 namespace.default.visible = true
 
 namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
 namespace.default.search.paths += /odm/${LIB}
 namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths +=           /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
 namespace.default.asan.search.paths += /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
 namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
 namespace.default.asan.search.paths +=           /vendor/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
-namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 # Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
 # If a shared library or an executable requests a shared library that
@@ -71,7 +72,7 @@
 # shared library cannot be loaded from the runtime namespace either, the
 # dynamic linker tries to load the shared library from the resolv namespace.
 # Finally, if all attempts fail, the dynamic linker returns an error.
-namespace.default.links = runtime,resolv
+namespace.default.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
@@ -90,6 +91,9 @@
 # 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
 #
@@ -123,12 +127,15 @@
 
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
-namespace.media.links = default
+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
 #
@@ -206,7 +213,7 @@
 # libs listed here can be used. Order is important here as the namespaces are
 # tried in this order. rs should be before vndk because both are capable
 # of loading libRS_internal.so
-namespace.sphal.links = rs,default,vndk
+namespace.sphal.links = rs,default,vndk,neuralnetworks
 
 # Renderscript gets separate namespace
 namespace.sphal.link.rs.shared_libs = libRS_internal.so
@@ -216,6 +223,9 @@
 
 namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
+# LLNDK library moved into apex
+namespace.sphal.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
 ###############################################################################
 # "rs" namespace
 #
@@ -255,7 +265,7 @@
 namespace.rs.asan.permitted.paths +=           /vendor/${LIB}
 namespace.rs.asan.permitted.paths += /data
 
-namespace.rs.links = default,vndk
+namespace.rs.links = default,vndk,neuralnetworks
 
 namespace.rs.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.rs.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
@@ -265,6 +275,9 @@
 
 namespace.rs.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
 
+# LLNDK library moved into apex
+namespace.rs.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
 ###############################################################################
 # "vndk" namespace
 #
@@ -309,10 +322,31 @@
 # When these NDK libs are required inside this namespace, then it is redirected
 # to the default namespace. This is possible since their ABI is stable across
 # Android releases.
-namespace.vndk.links = default
+namespace.vndk.links = default,neuralnetworks
 
 namespace.vndk.link.default.shared_libs  = %LLNDK_LIBRARIES%
 namespace.vndk.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
+namespace.vndk.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
+###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs  = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
 
 
 ###############################################################################
@@ -322,7 +356,7 @@
 # (LL-NDK only) access.
 ###############################################################################
 [vendor]
-additional.namespaces = runtime
+additional.namespaces = runtime,neuralnetworks
 
 namespace.default.isolated = false
 
@@ -336,8 +370,9 @@
 # Access to system libraries is allowed
 namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+# Put /system/lib/vndk at the last search order in vndk_lite for GSI
 namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
 
 namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
@@ -356,14 +391,14 @@
 namespace.default.asan.search.paths +=           /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.asan.search.paths += /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths +=           /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
 namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
 namespace.default.asan.search.paths +=           /system/${LIB}/vndk%VNDK_VER%
 
-namespace.default.links = runtime
+namespace.default.links = runtime,neuralnetworks
 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.
@@ -375,6 +410,9 @@
 # Workaround for b/124772622
 namespace.default.link.runtime.shared_libs += libandroidicu.so
 
+# LLNDK library moved into apex
+namespace.default.link.neuralnetworks.shared_libs = libneuralnetworks.so
+
 ###############################################################################
 # "runtime" APEX namespace
 #
@@ -391,13 +429,34 @@
 namespace.runtime.link.default.allow_all_shared_libs = true
 
 ###############################################################################
+# "neuralnetworks" APEX namespace
+#
+# This namespace is for libraries within the NNAPI APEX.
+###############################################################################
+namespace.neuralnetworks.isolated = true
+namespace.neuralnetworks.visible = true
+
+namespace.neuralnetworks.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.asan.search.paths = /apex/com.android.neuralnetworks/${LIB}
+namespace.neuralnetworks.links = default
+namespace.neuralnetworks.link.default.shared_libs  = libc.so
+namespace.neuralnetworks.link.default.shared_libs += libcgrouprc.so
+namespace.neuralnetworks.link.default.shared_libs += libdl.so
+namespace.neuralnetworks.link.default.shared_libs += liblog.so
+namespace.neuralnetworks.link.default.shared_libs += libm.so
+namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libsync.so
+namespace.neuralnetworks.link.default.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
+additional.namespaces = runtime,media,conscrypt,resolv,neuralnetworks
 
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
@@ -415,7 +474,7 @@
 namespace.default.asan.search.paths +=           /vendor/${LIB}
 
 # Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
-namespace.default.links = runtime,resolv
+namespace.default.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
@@ -432,6 +491,9 @@
 
 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
 #
@@ -448,7 +510,6 @@
 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
@@ -463,12 +524,15 @@
 
 namespace.media.permitted.paths = /apex/com.android.media/${LIB}/extractors
 
-namespace.media.links = default
+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
 #
@@ -504,6 +568,27 @@
 namespace.resolv.link.default.shared_libs += libbinder_ndk.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
@@ -514,5 +599,5 @@
 [postinstall]
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 455c9a8..93b7f43 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -6,6 +6,7 @@
     export ANDROID_DATA /data
     export ANDROID_STORAGE /storage
     export ANDROID_RUNTIME_ROOT /apex/com.android.runtime
+    export ANDROID_I18N_ROOT /apex/com.android.i18n
     export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
     export EXTERNAL_STORAGE /sdcard
     export ASEC_MOUNTPOINT /mnt/asec
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3acf301..0f61a61 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -38,6 +38,13 @@
     # Allow up to 32K FDs per process
     setrlimit nofile 32768 32768
 
+    # Create directory to keep ld.config.txt
+    mkdir /dev/linkerconfig 0755
+
+    # Generate ld.config.txt for early executed processes
+    exec -- /system/bin/linkerconfig --target /dev/linkerconfig/ld.config.txt
+    chmod 444 /dev/linkerconfig/ld.config.txt
+
     start ueventd
 
     # Run apexd-bootstrap so that APEXes that provide critical libraries
@@ -114,9 +121,16 @@
     mkdir /mnt/media_rw 0750 root media_rw
     mkdir /mnt/user 0755 root root
     mkdir /mnt/user/0 0755 root root
+    mkdir /mnt/user/0/self 0755 root root
+    mkdir /mnt/user/0/emulated 0755 root root
+    mkdir /mnt/user/0/emulated/0 0755 root root
     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
@@ -288,6 +302,11 @@
     chown system system /sys/power/wakeup_count
     chmod 0660 /sys/power/state
 
+    chown radio wakelock /sys/power/wake_lock
+    chown radio wakelock /sys/power/wake_unlock
+    chmod 0660 /sys/power/wake_lock
+    chmod 0660 /sys/power/wake_unlock
+
     # Start logd before any other services run to ensure we capture all of their logs.
     start logd
 
@@ -355,9 +374,6 @@
     # Once everything is setup, no need to modify /.
     # The bind+remount combination allows this to work in containers.
     mount rootfs rootfs / remount bind ro nodev
-    # Mount default storage into root namespace
-    mount none /mnt/runtime/default /storage bind rec
-    mount none none /storage slave rec
 
     # Make sure /sys/kernel/debug (if present) is labeled properly
     # Note that tracefs may be mounted under debug, so we need to cross filesystems
@@ -402,6 +418,8 @@
     mkdir /metadata/vold
     chmod 0700 /metadata/vold
     mkdir /metadata/password_slots 0771 root system
+    mkdir /metadata/ota 0700 root system
+    mkdir /metadata/ota/snapshots 0700 root system
 
     mkdir /metadata/apex 0700 root system
     mkdir /metadata/apex/sessions 0700 root system
@@ -413,6 +431,10 @@
     # HALs required before storage encryption can get unlocked (FBE/FDE)
     class_start early_hal
 
+    # Check and mark a successful boot, before mounting userdata with mount_all.
+    # No-op for non-A/B device.
+    exec_start update_verifier_nonencrypted
+
 on post-fs-data
     mark_post_data
 
@@ -495,7 +517,6 @@
     mkdir /data/misc/ethernet 0770 system system
     mkdir /data/misc/dhcp 0770 dhcp dhcp
     mkdir /data/misc/user 0771 root root
-    mkdir /data/misc/perfprofd 0775 root root
     # give system access to wpa_supplicant.conf for backup and restore
     chmod 0660 /data/misc/wifi/wpa_supplicant.conf
     mkdir /data/local 0751 root root
@@ -597,6 +618,9 @@
     mkdir /data/cache/backup_stage 0700 system system
     mkdir /data/cache/backup 0700 system system
 
+    mkdir /data/rollback 0700 system system
+    mkdir /data/rollback-observer 0700 system system
+
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status ready
     parse_apex_configs
@@ -625,25 +649,35 @@
     chown root system /dev/fscklogs/log
     chmod 0770 /dev/fscklogs/log
 
+# Switch between sdcardfs and FUSE depending on persist property
+# TODO: Move this to ro property before launch because FDE devices
+# interact with persistent properties differently during boot
+on zygote-start && property:persist.sys.fuse=true
+  # Mount default storage into root namespace
+  mount none /mnt/user/0 /storage bind rec
+  mount none none /storage slave rec
+on zygote-start && property:persist.sys.fuse=false
+  # Mount default storage into root namespace
+  mount none /mnt/runtime/default /storage bind rec
+  mount none none /storage slave rec
+on zygote-start && property:persist.sys.fuse=""
+  # Mount default storage into root namespace
+  mount none /mnt/runtime/default /storage bind rec
+  mount none none /storage slave rec
+
 # It is recommended to put unnecessary data/ initialization from post-fs-data
 # to start-zygote in device's init.rc to unblock zygote start.
 on zygote-start && property:ro.crypto.state=unencrypted
-    # A/B update verifier that marks a successful boot.
-    exec_start update_verifier_nonencrypted
     start netd
     start zygote
     start zygote_secondary
 
 on zygote-start && property:ro.crypto.state=unsupported
-    # A/B update verifier that marks a successful boot.
-    exec_start update_verifier_nonencrypted
     start netd
     start zygote
     start zygote_secondary
 
 on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
-    # A/B update verifier that marks a successful boot.
-    exec_start update_verifier_nonencrypted
     start netd
     start zygote
     start zygote_secondary
@@ -684,10 +718,6 @@
     chown radio system /sys/android_power/acquire_partial_wake_lock
     chown radio system /sys/android_power/release_wake_lock
     chown system system /sys/power/autosleep
-    chown radio wakelock /sys/power/wake_lock
-    chown radio wakelock /sys/power/wake_unlock
-    chmod 0660 /sys/power/wake_lock
-    chmod 0660 /sys/power/wake_unlock
 
     chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate
     chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_rate
@@ -770,8 +800,6 @@
     trigger zygote-start
 
 on property:vold.decrypt=trigger_restart_min_framework
-    # A/B update verifier that marks a successful boot.
-    exec_start update_verifier
     class_start main
 
 on property:vold.decrypt=trigger_restart_framework
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index f62c3df..dbe60e5 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -40,6 +40,7 @@
 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
+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
 endif
@@ -65,6 +66,10 @@
   vndksp_libraries_list := $(VNDK_SAMEPROCESS_LIBRARIES)
 endif
 
+# LLNDK libraries that has been moved to an apex package and no longer are present on
+# /system image.
+llndk_libraries_moved_to_apex_list:=$(LLNDK_MOVED_TO_APEX_LIBRARIES)
+
 # $(1): list of libraries
 # $(2): output file to write the list of libraries to
 define write-libs-to-file
@@ -88,9 +93,17 @@
 # $(2): output file with the filtered list of lib names
 $(LOCAL_BUILT_MODULE): private-filter-out-private-libs = \
   paste -sd ":" $(1) > $(2) && \
-  cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | xargs -n 1 -I privatelib bash -c "sed -i.bak 's/privatelib//' $(2)" && \
+  while read -r privatelib; do sed -i.bak "s/$$privatelib//" $(2) ; done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
   sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(2) && \
   rm -f $(2).bak
+
+# # Given a file with a list of libs in "a:b:c" format, filter-out the LLNDK libraries migrated into apex file
+# # and write resulting list to a new file in "a:b:c" format
+ $(LOCAL_BUILT_MODULE): private-filter-out-llndk-in-apex-libs = \
+   for lib in $(PRIVATE_LLNDK_LIBRARIES_MOVED_TO_APEX_LIST); do sed -i.bak s/$$lib.so// $(1); done && \
+   sed -i.bak -e 's/::\+/:/g ; s/^:\+// ; s/:\+$$//' $(1) && \
+   rm -f $(1).bak
+
 $(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_FILE := $(llndk_libraries_file)
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SP_LIBRARIES_FILE := $(vndksp_libraries_file)
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES_FILE := $(vndkcore_libraries_file)
@@ -100,6 +113,7 @@
 $(LOCAL_BUILT_MODULE): PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
 $(LOCAL_BUILT_MODULE): PRIVATE_COMP_CHECK_SCRIPT := $(compatibility_check_script)
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_TAG := \#VNDK$(vndk_version)\#
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES_MOVED_TO_APEX_LIST := $(llndk_libraries_moved_to_apex_list)
 deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
   $(vndkprivate_libraries_file)
 ifeq ($(check_backward_compatibility),true)
@@ -118,6 +132,7 @@
 endif
 	@mkdir -p $(dir $@)
 	$(call private-filter-out-private-libs,$(PRIVATE_LLNDK_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
+	$(call private-filter-out-llndk-in-apex-libs,$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
 	$(hide) sed -e "s?%LLNDK_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)?g" $< >$@
 	$(call private-filter-out-private-libs,$(PRIVATE_VNDK_SP_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)
 	$(hide) sed -i.bak -e "s?%VNDK_SAMEPROCESS_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/vndksp_filtered)?g" $@
@@ -139,20 +154,16 @@
 endif
 
 	$(hide) echo -n > $(PRIVATE_INTERMEDIATES_DIR)/private_llndk && \
-	cat $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) | \
-	xargs -n 1 -I privatelib bash -c "(grep privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk" && \
+	while read -r privatelib; \
+	do (grep $$privatelib $(PRIVATE_LLNDK_LIBRARIES_FILE) || true) >> $(PRIVATE_INTERMEDIATES_DIR)/private_llndk ; \
+	done < $(PRIVATE_VNDK_PRIVATE_LIBRARIES_FILE) && \
 	paste -sd ":" $(PRIVATE_INTERMEDIATES_DIR)/private_llndk | \
 	sed -i.bak -e "s?%PRIVATE_LLNDK_LIBRARIES%?$$(cat -)?g" $@
 
 	$(hide) sed -i.bak -e "s?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g" $@
 	$(hide) sed -i.bak -e "s?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g" $@
 	$(hide) sed -i.bak -e "s?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g" $@
-ifeq ($(TARGET_COPY_OUT_PRODUCT),$(TARGET_COPY_OUT_PRODUCT_SERVICES))
-	# Remove lines containing %PRODUCT_SERVICES% (identical to the %PRODUCT% ones)
-	$(hide) sed -i.bak -e "\?%PRODUCT_SERVICES%?d" $@
-else
-	$(hide) sed -i.bak -e "s?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g" $@
-endif
+	$(hide) sed -i.bak -e "s?%SYSTEM_EXT%?$(TARGET_COPY_OUT_SYSTEM_EXT)?g" $@
 	$(hide) sed -i.bak -e "s?^$(PRIVATE_VNDK_VERSION_TAG)??g" $@
 	$(hide) sed -i.bak "/^\#VNDK[0-9]\{2\}\#.*$$/d" $@
 	$(hide) rm -f $@.bak
@@ -166,6 +177,7 @@
 intermediates_dir :=
 library_lists_dir :=
 llndk_libraries_file :=
+llndk_moved_to_apex_libraries_file :=
 vndksp_libraries_file :=
 vndkcore_libraries_file :=
 vndkprivate_libraries_file :=
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 3bc3883..694b50e 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -13,7 +13,7 @@
         "auditctl",
         "awk",
         "bzip2",
-        "grep",
+        "ldd",
         "logwrapper",
         "mini-keyctl",
         "mkshrc",
@@ -32,7 +32,6 @@
 phony {
     name: "shell_and_utilities_recovery",
     required: [
-        "grep.recovery",
         "sh.recovery",
         "toolbox.recovery",
         "toybox.recovery",
@@ -44,7 +43,6 @@
     name: "shell_and_utilities_vendor",
     required: [
         "awk_vendor",
-        "grep_vendor",
         "logwrapper_vendor",
         "mkshrc_vendor",
         "sh_vendor",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 1926a4f..d391cc1 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -214,7 +214,7 @@
 
 ## Android R
 
-BSD: grep fsck\_msdos newfs\_msdos
+BSD: fsck\_msdos newfs\_msdos
 
 bzip2: bzcat bzip2 bunzip2
 
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 6897663..1d934a2 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -164,8 +164,10 @@
 }
 
 void storaged_t::add_user_ce(userid_t user_id) {
-    load_proto(user_id);
-    proto_loaded[user_id] = true;
+    if (!proto_loaded[user_id]) {
+        load_proto(user_id);
+        proto_loaded[user_id] = true;
+    }
 }
 
 void storaged_t::remove_user_ce(userid_t user_id) {
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index ca2421b..6668cf3 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -41,7 +41,6 @@
 using android::hardware::health::V2_0::StorageInfo;
 
 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
-const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
 const char* emmc_info_t::emmc_ver_str[9] = {
     "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
 };
@@ -62,10 +61,8 @@
     if (healthService != nullptr) {
         return new health_storage_info_t(healthService);
     }
-    if (FileExists(emmc_info_t::emmc_sysfs) ||
-        FileExists(emmc_info_t::emmc_debugfs)) {
-        return new emmc_info_t;
-    }
+    if (FileExists(emmc_info_t::emmc_sysfs)) return new emmc_info_t;
+
     if (FileExists(ufs_info_t::health_file)) {
         return new ufs_info_t;
     }
@@ -241,8 +238,7 @@
 
 void emmc_info_t::report()
 {
-    if (!report_sysfs() && !report_debugfs())
-        return;
+    if (!report_sysfs()) return;
 
     publish();
 }
@@ -284,54 +280,6 @@
     return true;
 }
 
-namespace {
-
-const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
-/* 2 characters in string for each byte */
-const size_t EXT_CSD_REV_IDX = 192 * 2;
-const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
-const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
-const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
-
-} // namespace
-
-bool emmc_info_t::report_debugfs()
-{
-    string buffer;
-    uint16_t rev = 0;
-
-    if (!ReadFileToString(emmc_debugfs, &buffer) ||
-        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
-        return false;
-    }
-
-    string str = buffer.substr(EXT_CSD_REV_IDX, 2);
-    if (!ParseUint(str, &rev) ||
-        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
-        return false;
-    }
-
-    version = "emmc ";
-    version += emmc_ver_str[rev];
-
-    str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
-    if (!ParseUint(str, &eol)) {
-        return false;
-    }
-
-    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
-    if (!ParseUint(str, &lifetime_a)) {
-        return false;
-    }
-
-    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
-    if (!ParseUint(str, &lifetime_b)) {
-        return false;
-    }
-
-    return true;
-}
-
 void ufs_info_t::report()
 {
     string buffer;
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 9ca5607..4ca5f5a 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -1,14 +1,10 @@
 cc_defaults {
     name: "toolbox_defaults",
-
     cflags: [
         "-Werror",
         "-Wno-unused-parameter",
         "-Wno-unused-const-variable",
         "-D_FILE_OFFSET_BITS=64",
-        "-DWITHOUT_NLS",
-        "-DWITHOUT_BZ2",
-        "-DWITHOUT_GZIP",
     ],
 }
 
@@ -28,6 +24,7 @@
         "toolbox.c",
         "getevent.c",
         "getprop.cpp",
+        "modprobe.cpp",
         "setprop.cpp",
         "start.cpp",
     ],
@@ -37,11 +34,15 @@
     shared_libs: [
         "libbase",
     ],
-    static_libs: ["libpropertyinfoparser"],
+    static_libs: [
+        "libmodprobe",
+        "libpropertyinfoparser",
+    ],
 
     symlinks: [
         "getevent",
         "getprop",
+        "modprobe",
         "setprop",
         "start",
         "stop",
@@ -60,39 +61,3 @@
     vendor: true,
     defaults: ["toolbox_binary_defaults"],
 }
-
-// We build BSD grep separately (but see http://b/111849261).
-cc_defaults {
-    name: "grep_common",
-    defaults: ["toolbox_defaults"],
-    srcs: [
-        "upstream-netbsd/usr.bin/grep/fastgrep.c",
-        "upstream-netbsd/usr.bin/grep/file.c",
-        "upstream-netbsd/usr.bin/grep/grep.c",
-        "upstream-netbsd/usr.bin/grep/queue.c",
-        "upstream-netbsd/usr.bin/grep/util.c",
-    ],
-    symlinks: [
-        "egrep",
-        "fgrep",
-    ],
-    sanitize: {
-        integer_overflow: false,
-    },
-}
-
-cc_binary {
-    name: "grep",
-    defaults: ["grep_common"],
-    recovery_available: true,
-}
-
-// Build vendor grep.
-// TODO: Add vendor_available to "grep" module and remove "grep_vendor" module
-//       when vendor_available is fully supported.
-cc_binary {
-    name: "grep_vendor",
-    stem: "grep",
-    vendor: true,
-    defaults: ["grep_common"],
-}
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
new file mode 100644
index 0000000..1b5f54e
--- /dev/null
+++ b/toolbox/modprobe.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <iostream>
+
+#include <android-base/strings.h>
+#include <modprobe/modprobe.h>
+
+enum modprobe_mode {
+    AddModulesMode,
+    RemoveModulesMode,
+    ListModulesMode,
+    ShowDependenciesMode,
+};
+
+static void print_usage(void) {
+    std::cerr << "Usage:" << std::endl;
+    std::cerr << std::endl;
+    std::cerr << "  modprobe [-alrqvsDb] [-d DIR] [MODULE]+" << std::endl;
+    std::cerr << "  modprobe [-alrqvsDb] [-d DIR] MODULE [symbol=value][...]" << std::endl;
+    std::cerr << std::endl;
+    std::cerr << "Options:" << std::endl;
+    std::cerr << "  -b: Apply blacklist to module names too" << std::endl;
+    std::cerr << "  -d: Load modules from DIR, option may be used multiple times" << std::endl;
+    std::cerr << "  -D: Print dependencies for modules only, do not load";
+    std::cerr << "  -h: Print this help" << std::endl;
+    std::cerr << "  -l: List modules matching pattern" << std::endl;
+    std::cerr << "  -r: Remove MODULE (multiple modules may be specified)" << std::endl;
+    std::cerr << "  -q: Quiet" << std::endl;
+    std::cerr << "  -v: Verbose" << std::endl;
+    std::cerr << std::endl;
+}
+
+#define check_mode()                                                      \
+    if (mode != AddModulesMode) {                                         \
+        std::cerr << "Error, multiple mode flags specified" << std::endl; \
+        print_usage();                                                    \
+        return EXIT_FAILURE;                                              \
+    }
+
+extern "C" int modprobe_main(int argc, char** argv) {
+    std::vector<std::string> modules;
+    std::string module_parameters;
+    std::vector<std::string> mod_dirs;
+    modprobe_mode mode = AddModulesMode;
+    bool blacklist = false;
+    bool verbose = false;
+    int rv = EXIT_SUCCESS;
+
+    int opt;
+    while ((opt = getopt(argc, argv, "abd:Dhlqrv")) != -1) {
+        switch (opt) {
+            case 'a':
+                // toybox modprobe supported -a to load multiple modules, this
+                // is supported here by default, ignore flag
+                check_mode();
+                break;
+            case 'b':
+                blacklist = true;
+                break;
+            case 'd':
+                mod_dirs.emplace_back(optarg);
+                break;
+            case 'D':
+                check_mode();
+                mode = ShowDependenciesMode;
+                break;
+            case 'h':
+                print_usage();
+                return EXIT_SUCCESS;
+            case 'l':
+                check_mode();
+                mode = ListModulesMode;
+                break;
+            case 'q':
+                verbose = false;
+                break;
+            case 'r':
+                check_mode();
+                mode = RemoveModulesMode;
+                break;
+            case 'v':
+                verbose = true;
+                break;
+            default:
+                std::cerr << "Unrecognized option: " << opt << std::endl;
+                return EXIT_FAILURE;
+        }
+    }
+
+    int parameter_count = 0;
+    for (opt = optind; opt < argc; opt++) {
+        if (!strchr(argv[opt], '=')) {
+            modules.emplace_back(argv[opt]);
+        } else {
+            parameter_count++;
+            if (module_parameters.empty()) {
+                module_parameters = argv[opt];
+            } else {
+                module_parameters = module_parameters + " " + argv[opt];
+            }
+        }
+    }
+
+    if (verbose) {
+        std::cout << "mode is " << mode << std::endl;
+        std::cout << "verbose is " << verbose << std::endl;
+        std::cout << "mod_dirs is: " << android::base::Join(mod_dirs, "") << std::endl;
+        std::cout << "modules is: " << android::base::Join(modules, "") << std::endl;
+        std::cout << "module parameters is: " << android::base::Join(module_parameters, "")
+                  << std::endl;
+    }
+
+    if (modules.empty()) {
+        if (mode == ListModulesMode) {
+            // emulate toybox modprobe list with no pattern (list all)
+            modules.emplace_back("*");
+        } else {
+            std::cerr << "No modules given." << std::endl;
+            print_usage();
+            return EXIT_FAILURE;
+        }
+    }
+    if (mod_dirs.empty()) {
+        std::cerr << "No module configuration directories given." << std::endl;
+        print_usage();
+        return EXIT_FAILURE;
+    }
+    if (parameter_count && modules.size() > 1) {
+        std::cerr << "Only one module may be loaded when specifying module parameters."
+                  << std::endl;
+        print_usage();
+        return EXIT_FAILURE;
+    }
+
+    Modprobe m(mod_dirs);
+    m.EnableVerbose(verbose);
+    if (blacklist) {
+        m.EnableBlacklist(true);
+    }
+
+    for (const auto& module : modules) {
+        switch (mode) {
+            case AddModulesMode:
+                if (!m.LoadWithAliases(module, true, module_parameters)) {
+                    std::cerr << "Failed to load module " << module;
+                    rv = EXIT_FAILURE;
+                }
+                break;
+            case RemoveModulesMode:
+                if (!m.Remove(module)) {
+                    std::cerr << "Failed to remove module " << module;
+                    rv = EXIT_FAILURE;
+                }
+                break;
+            case ListModulesMode: {
+                std::vector<std::string> list = m.ListModules(module);
+                std::cout << android::base::Join(list, "\n") << std::endl;
+                break;
+            }
+            case ShowDependenciesMode: {
+                std::vector<std::string> pre_deps;
+                std::vector<std::string> deps;
+                std::vector<std::string> post_deps;
+                if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {
+                    rv = EXIT_FAILURE;
+                    break;
+                }
+                std::cout << "Dependencies for " << module << ":" << std::endl;
+                std::cout << "Soft pre-dependencies:" << std::endl;
+                std::cout << android::base::Join(pre_deps, "\n") << std::endl;
+                std::cout << "Hard dependencies:" << std::endl;
+                std::cout << android::base::Join(deps, "\n") << std::endl;
+                std::cout << "Soft post-dependencies:" << std::endl;
+                std::cout << android::base::Join(post_deps, "\n") << std::endl;
+                break;
+            }
+            default:
+                std::cerr << "Bad mode";
+                rv = EXIT_FAILURE;
+        }
+    }
+
+    return rv;
+}
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 9a7ebd2..bb57e67 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,5 +1,6 @@
 TOOL(getevent)
 TOOL(getprop)
+TOOL(modprobe)
 TOOL(setprop)
 TOOL(start)
 TOOL(stop)
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
deleted file mode 100644
index 2fcd864..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/*	$OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * XXX: This file is a speed up for grep to cover the defects of the
- * regex library.  These optimizations should practically be implemented
- * there keeping this code clean.  This is a future TODO, but for the
- * meantime, we need to use this workaround.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $");
-
-#include <limits.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-static inline int	grep_cmp(const unsigned char *, const unsigned char *, size_t);
-static inline void	grep_revstr(unsigned char *, int);
-
-void
-fgrepcomp(fastgrep_t *fg, const char *pat)
-{
-	unsigned int i;
-
-	/* Initialize. */
-	fg->len = strlen(pat);
-	fg->bol = false;
-	fg->eol = false;
-	fg->reversed = false;
-
-	fg->pattern = (unsigned char *)grep_strdup(pat);
-
-	/* Preprocess pattern. */
-	for (i = 0; i <= UCHAR_MAX; i++)
-		fg->qsBc[i] = fg->len;
-	for (i = 1; i < fg->len; i++)
-		fg->qsBc[fg->pattern[i]] = fg->len - i;
-}
-
-/*
- * Returns: -1 on failure, 0 on success
- */
-int
-fastcomp(fastgrep_t *fg, const char *pat)
-{
-	unsigned int i;
-	int firstHalfDot = -1;
-	int firstLastHalfDot = -1;
-	int hasDot = 0;
-	int lastHalfDot = 0;
-	int shiftPatternLen;
-
-	/* Initialize. */
-	fg->len = strlen(pat);
-	fg->bol = false;
-	fg->eol = false;
-	fg->reversed = false;
-	fg->word = wflag;
-
-	/* Remove end-of-line character ('$'). */
-	if (fg->len > 0 && pat[fg->len - 1] == '$') {
-		fg->eol = true;
-		fg->len--;
-	}
-
-	/* Remove beginning-of-line character ('^'). */
-	if (pat[0] == '^') {
-		fg->bol = true;
-		fg->len--;
-		pat++;
-	}
-
-	if (fg->len >= 14 &&
-	    memcmp(pat, "[[:<:]]", 7) == 0 &&
-	    memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) {
-		fg->len -= 14;
-		pat += 7;
-		/* Word boundary is handled separately in util.c */
-		fg->word = true;
-	}
-
-	/*
-	 * pat has been adjusted earlier to not include '^', '$' or
-	 * the word match character classes at the beginning and ending
-	 * of the string respectively.
-	 */
-	fg->pattern = grep_malloc(fg->len + 1);
-	memcpy(fg->pattern, pat, fg->len);
-	fg->pattern[fg->len] = '\0';
-
-	/* Look for ways to cheat...er...avoid the full regex engine. */
-	for (i = 0; i < fg->len; i++) {
-		/* Can still cheat? */
-		if (fg->pattern[i] == '.') {
-			hasDot = i;
-			if (i < fg->len / 2) {
-				if (firstHalfDot < 0)
-					/* Closest dot to the beginning */
-					firstHalfDot = i;
-			} else {
-				/* Closest dot to the end of the pattern. */
-				lastHalfDot = i;
-				if (firstLastHalfDot < 0)
-					firstLastHalfDot = i;
-			}
-		} else {
-			/* Free memory and let others know this is empty. */
-			free(fg->pattern);
-			fg->pattern = NULL;
-			return (-1);
-		}
-	}
-
-	/*
-	 * Determine if a reverse search would be faster based on the placement
-	 * of the dots.
-	 */
-	if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) &&
-	    ((lastHalfDot) && ((firstHalfDot < 0) ||
-	    ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) &&
-	    !oflag && !color) {
-		fg->reversed = true;
-		hasDot = fg->len - (firstHalfDot < 0 ?
-		    firstLastHalfDot : firstHalfDot) - 1;
-		grep_revstr(fg->pattern, fg->len);
-	}
-
-	/*
-	 * Normal Quick Search would require a shift based on the position the
-	 * next character after the comparison is within the pattern.  With
-	 * wildcards, the position of the last dot effects the maximum shift
-	 * distance.
-	 * The closer to the end the wild card is the slower the search.  A
-	 * reverse version of this algorithm would be useful for wildcards near
-	 * the end of the string.
-	 *
-	 * Examples:
-	 * Pattern	Max shift
-	 * -------	---------
-	 * this		5
-	 * .his		4
-	 * t.is		3
-	 * th.s		2
-	 * thi.		1
-	 */
-
-	/* Adjust the shift based on location of the last dot ('.'). */
-	shiftPatternLen = fg->len - hasDot;
-
-	/* Preprocess pattern. */
-	for (i = 0; i <= (signed)UCHAR_MAX; i++)
-		fg->qsBc[i] = shiftPatternLen;
-	for (i = hasDot + 1; i < fg->len; i++) {
-		fg->qsBc[fg->pattern[i]] = fg->len - i;
-	}
-
-	/*
-	 * Put pattern back to normal after pre-processing to allow for easy
-	 * comparisons later.
-	 */
-	if (fg->reversed)
-		grep_revstr(fg->pattern, fg->len);
-
-	return (0);
-}
-
-int
-grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch)
-{
-	unsigned int j;
-	int ret = REG_NOMATCH;
-
-	if (pmatch->rm_so == (ssize_t)len)
-		return (ret);
-
-	if (fg->bol && pmatch->rm_so != 0) {
-		pmatch->rm_so = len;
-		pmatch->rm_eo = len;
-		return (ret);
-	}
-
-	/* No point in going farther if we do not have enough data. */
-	if (len < fg->len)
-		return (ret);
-
-	/* Only try once at the beginning or ending of the line. */
-	if (fg->bol || fg->eol) {
-		/* Simple text comparison. */
-		/* Verify data is >= pattern length before searching on it. */
-		if (len >= fg->len) {
-			/* Determine where in data to start search at. */
-			j = fg->eol ? len - fg->len : 0;
-			if (!((fg->bol && fg->eol) && (len != fg->len)))
-				if (grep_cmp(fg->pattern, data + j,
-				    fg->len) == -1) {
-					pmatch->rm_so = j;
-					pmatch->rm_eo = j + fg->len;
-						ret = 0;
-				}
-		}
-	} else if (fg->reversed) {
-		/* Quick Search algorithm. */
-		j = len;
-		do {
-			if (grep_cmp(fg->pattern, data + j - fg->len,
-				fg->len) == -1) {
-				pmatch->rm_so = j - fg->len;
-				pmatch->rm_eo = j;
-				ret = 0;
-				break;
-			}
-			/* Shift if within bounds, otherwise, we are done. */
-			if (j == fg->len)
-				break;
-			j -= fg->qsBc[data[j - fg->len - 1]];
-		} while (j >= fg->len);
-	} else {
-		/* Quick Search algorithm. */
-		j = pmatch->rm_so;
-		do {
-			if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
-				pmatch->rm_so = j;
-				pmatch->rm_eo = j + fg->len;
-				ret = 0;
-				break;
-			}
-
-			/* Shift if within bounds, otherwise, we are done. */
-			if (j + fg->len == len)
-				break;
-			else
-				j += fg->qsBc[data[j + fg->len]];
-		} while (j <= (len - fg->len));
-	}
-
-	return (ret);
-}
-
-/*
- * Returns:	i >= 0 on failure (position that it failed)
- *		-1 on success
- */
-static inline int
-grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len)
-{
-	size_t size;
-	wchar_t *wdata, *wpat;
-	unsigned int i;
-
-	if (iflag) {
-		if ((size = mbstowcs(NULL, (const char *)data, 0)) ==
-		    ((size_t) - 1))
-			return (-1);
-
-		wdata = grep_malloc(size * sizeof(wint_t));
-
-		if (mbstowcs(wdata, (const char *)data, size) ==
-		    ((size_t) - 1))
-			return (-1);
-
-		if ((size = mbstowcs(NULL, (const char *)pat, 0)) ==
-		    ((size_t) - 1))
-			return (-1);
-
-		wpat = grep_malloc(size * sizeof(wint_t));
-
-		if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1))
-			return (-1);
-		for (i = 0; i < len; i++) {
-			if ((towlower(wpat[i]) == towlower(wdata[i])) ||
-			    ((grepbehave != GREP_FIXED) && wpat[i] == L'.'))
-				continue;
-			free(wpat);
-			free(wdata);
-				return (i);
-		}
-	} else {
-		for (i = 0; i < len; i++) {
-			if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) &&
-			    pat[i] == '.'))
-				continue;
-			return (i);
-		}
-	}
-	return (-1);
-}
-
-static inline void
-grep_revstr(unsigned char *str, int len)
-{
-	int i;
-	char c;
-
-	for (i = 0; i < len / 2; i++) {
-		c = str[i];
-		str[i] = str[len - i - 1];
-		str[len - i - 1] = c;
-	}
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
deleted file mode 100644
index 428bf58..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/file.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/*	$NetBSD: file.c,v 1.10 2018/08/12 09:03:21 christos Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $	*/
-/*	$OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $	*/
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
- * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: file.c,v 1.10 2018/08/12 09:03:21 christos Exp $");
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-#define	MAXBUFSIZ	(32 * 1024)
-#define	LNBUFBUMP	80
-
-#ifndef WITHOUT_GZIP
-static gzFile gzbufdesc;
-#endif
-#ifndef WITHOUT_BZ2
-static BZFILE* bzbufdesc;
-#endif
-
-static unsigned char buffer[MAXBUFSIZ + 1];
-static unsigned char *bufpos;
-static size_t bufrem;
-
-static unsigned char *lnbuf;
-static size_t lnbuflen;
-
-static inline int
-grep_refill(struct file *f)
-{
-	ssize_t nr = -1;
-	int bzerr;
-
-	bufpos = buffer;
-	bufrem = 0;
-
-#ifndef WITHOUT_GZIP
-	if (filebehave == FILE_GZIP) {
-		nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
-		if (nr == -1)
-			return -1;
-	}
-#endif
-#ifndef WITHOUT_BZ2
-	if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
-		nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
-		switch (bzerr) {
-		case BZ_OK:
-		case BZ_STREAM_END:
-			/* No problem, nr will be okay */
-			break;
-		case BZ_DATA_ERROR_MAGIC:
-			/*
-			 * As opposed to gzread(), which simply returns the
-			 * plain file data, if it is not in the correct
-			 * compressed format, BZ2_bzRead() instead aborts.
-			 *
-			 * So, just restart at the beginning of the file again,
-			 * and use plain reads from now on.
-			 */
-			BZ2_bzReadClose(&bzerr, bzbufdesc);
-			bzbufdesc = NULL;
-			if (lseek(f->fd, 0, SEEK_SET) == -1)
-				return (-1);
-			nr = read(f->fd, buffer, MAXBUFSIZ);
-			break;
-		default:
-			/* Make sure we exit with an error */
-			nr = -1;
-		}
-		if (nr == -1)
-			return -1;
-	}
-#endif
-	if (nr == -1) {
-		nr = read(f->fd, buffer, MAXBUFSIZ);
-	}
-
-	if (nr < 0)
-		return (-1);
-
-	bufrem = nr;
-	return (0);
-}
-
-static inline void
-grep_lnbufgrow(size_t newlen)
-{
-
-	if (lnbuflen < newlen) {
-		lnbuf = grep_realloc(lnbuf, newlen);
-		lnbuflen = newlen;
-	}
-}
-
-char *
-grep_fgetln(struct file *f, size_t *lenp)
-{
-	unsigned char *p;
-	char *ret;
-	size_t len;
-	size_t off;
-	ptrdiff_t diff;
-
-	/* Fill the buffer, if necessary */
-	if (bufrem == 0 && grep_refill(f) != 0)
-		goto error;
-
-	if (bufrem == 0) {
-		/* Return zero length to indicate EOF */
-		*lenp = 0;
-		return ((char *)bufpos);
-	}
-
-	/* Look for a newline in the remaining part of the buffer */
-	if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
-		++p; /* advance over newline */
-		len = p - bufpos;
-		grep_lnbufgrow(len + 1);
-		memcpy(lnbuf, bufpos, len);
-		lnbuf[len] = '\0';
-		*lenp = len;
-		bufrem -= len;
-		bufpos = p;
-		return ((char *)lnbuf);
-	}
-
-	/* We have to copy the current buffered data to the line buffer */
-	for (len = bufrem, off = 0; ; len += bufrem) {
-		/* Make sure there is room for more data */
-		grep_lnbufgrow(len + LNBUFBUMP);
-		memcpy(lnbuf + off, bufpos, len - off);
-		lnbuf[len] = '\0';
-		off = len;
-		if (grep_refill(f) != 0)
-			goto error;
-		if (bufrem == 0)
-			/* EOF: return partial line */
-			break;
-		if ((p = memchr(bufpos, line_sep, bufrem)) == NULL)
-			continue;
-		/* got it: finish up the line (like code above) */
-		++p;
-		diff = p - bufpos;
-		len += diff;
-		grep_lnbufgrow(len + 1);
-		memcpy(lnbuf + off, bufpos, diff);
-		lnbuf[off + diff] = '\0';
-		bufrem -= diff;
-		bufpos = p;
-		break;
-	}
-	*lenp = len;
-	return ((char *)lnbuf);
-
-error:
-	*lenp = 0;
-	return (NULL);
-}
-
-static inline struct file *
-grep_file_init(struct file *f)
-{
-
-#ifndef WITHOUT_GZIP
-	if (filebehave == FILE_GZIP &&
-	    (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
-		goto error;
-#endif
-
-#ifndef WITHOUT_BZ2
-	if (filebehave == FILE_BZIP &&
-	    (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
-		goto error;
-#endif
-
-	/* Fill read buffer, also catches errors early */
-	if (grep_refill(f) != 0)
-		goto error;
-
-	/* Check for binary stuff, if necessary */
-	if (!nulldataflag && binbehave != BINFILE_TEXT &&
-	    memchr(bufpos, '\0', bufrem) != NULL)
-		f->binary = true;
-
-	return (f);
-error:
-	close(f->fd);
-	free(f);
-	return (NULL);
-}
-
-/*
- * Opens a file for processing.
- */
-struct file *
-grep_open(const char *path)
-{
-	struct file *f;
-
-	f = grep_malloc(sizeof *f);
-	memset(f, 0, sizeof *f);
-	if (path == NULL) {
-		/* Processing stdin implies --line-buffered. */
-		lbflag = true;
-		f->fd = STDIN_FILENO;
-	} else if ((f->fd = open(path, O_RDONLY)) == -1) {
-		free(f);
-		return (NULL);
-	}
-
-	return (grep_file_init(f));
-}
-
-/*
- * Closes a file.
- */
-void
-grep_close(struct file *f)
-{
-
-	close(f->fd);
-
-	/* Reset read buffer and line buffer */
-	bufpos = buffer;
-	bufrem = 0;
-
-	free(lnbuf);
-	lnbuf = NULL;
-	lnbuflen = 0;
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
deleted file mode 100644
index bad2a73..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.c
+++ /dev/null
@@ -1,722 +0,0 @@
-/*	$NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 christos Exp $	*/
-/* 	$FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $	*/
-/*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: grep.c,v 1.15 2018/08/12 09:03:21 christos Exp $");
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <getopt.h>
-#include <limits.h>
-#include <libgen.h>
-#include <locale.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "grep.h"
-
-#ifndef WITHOUT_NLS
-#include <nl_types.h>
-nl_catd	 catalog;
-#endif
-
-/*
- * Default messags to use when NLS is disabled or no catalogue
- * is found.
- */
-const char	*errstr[] = {
-	"",
-/* 1*/	"(standard input)",
-/* 2*/	"cannot read bzip2 compressed file",
-/* 3*/	"unknown %s option",
-/* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
-/* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
-/* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
-/* 7*/	"\t[pattern] [file ...]\n",
-/* 8*/	"Binary file %s matches\n",
-/* 9*/	"%s (BSD grep) %s\n",
-};
-
-/* Flags passed to regcomp() and regexec() */
-int		 cflags = 0;
-int		 eflags = REG_STARTEND;
-
-/* Searching patterns */
-unsigned int	 patterns, pattern_sz;
-char		**pattern;
-regex_t		*r_pattern;
-fastgrep_t	*fg_pattern;
-
-/* Filename exclusion/inclusion patterns */
-unsigned int	 fpatterns, fpattern_sz;
-unsigned int	 dpatterns, dpattern_sz;
-struct epat	*dpattern, *fpattern;
-
-/* For regex errors  */
-char	 re_error[RE_ERROR_BUF + 1];
-
-/* Command-line flags */
-unsigned long long Aflag;	/* -A x: print x lines trailing each match */
-unsigned long long Bflag;	/* -B x: print x lines leading each match */
-bool	 Hflag;		/* -H: always print file name */
-bool	 Lflag;		/* -L: only show names of files with no matches */
-bool	 bflag;		/* -b: show block numbers for each match */
-bool	 cflag;		/* -c: only show a count of matching lines */
-bool	 hflag;		/* -h: don't print filename headers */
-bool	 iflag;		/* -i: ignore case */
-bool	 lflag;		/* -l: only show names of files with matches */
-bool	 mflag;		/* -m x: stop reading the files after x matches */
-unsigned long long mcount;	/* count for -m */
-bool	 nflag;		/* -n: show line numbers in front of matching lines */
-bool	 oflag;		/* -o: print only matching part */
-bool	 qflag;		/* -q: quiet mode (don't output anything) */
-bool	 sflag;		/* -s: silent mode (ignore errors) */
-bool	 vflag;		/* -v: only show non-matching lines */
-bool	 wflag;		/* -w: pattern must start and end on word boundaries */
-bool	 xflag;		/* -x: pattern must match entire line */
-bool	 lbflag;	/* --line-buffered */
-bool	 nullflag;	/* --null */
-bool	 nulldataflag;	/* --null-data */
-unsigned char line_sep = '\n';	/* 0 for --null-data */
-char	*label;		/* --label */
-const char *color;	/* --color */
-int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
-int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
-int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
-int	 devbehave = DEV_READ;		/* -D: handling of devices */
-int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
-int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
-
-bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
-bool	 fexclude, finclude;	/* --exclude and --include */
-
-enum {
-	BIN_OPT = CHAR_MAX + 1,
-	COLOR_OPT,
-	DECOMPRESS_OPT,
-	HELP_OPT,
-	MMAP_OPT,
-	LINEBUF_OPT,
-	LABEL_OPT,
-	R_EXCLUDE_OPT,
-	R_INCLUDE_OPT,
-	R_DEXCLUDE_OPT,
-	R_DINCLUDE_OPT
-};
-
-static inline const char	*init_color(const char *);
-
-/* Housekeeping */
-int	 tail;		/* lines left to print */
-bool	 notfound;	/* file not found */
-
-extern char	*__progname;
-
-/*
- * Prints usage information and returns 2.
- */
-__dead static void
-usage(void)
-{
-	fprintf(stderr, getstr(4), __progname);
-	fprintf(stderr, "%s", getstr(5));
-	fprintf(stderr, "%s", getstr(6));
-	fprintf(stderr, "%s", getstr(7));
-	exit(2);
-}
-
-static const char optstr[] =
-    "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
-
-struct option long_options[] =
-{
-	{"binary-files",	required_argument,	NULL, BIN_OPT},
-#ifndef WITHOUT_GZIP
-	{"decompress",          no_argument,            NULL, DECOMPRESS_OPT},
-#endif
-	{"help",		no_argument,		NULL, HELP_OPT},
-	{"mmap",		no_argument,		NULL, MMAP_OPT},
-	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
-	{"label",		required_argument,	NULL, LABEL_OPT},
-	{"color",		optional_argument,	NULL, COLOR_OPT},
-	{"colour",		optional_argument,	NULL, COLOR_OPT},
-	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
-	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
-	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
-	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
-	{"after-context",	required_argument,	NULL, 'A'},
-	{"text",		no_argument,		NULL, 'a'},
-	{"before-context",	required_argument,	NULL, 'B'},
-	{"byte-offset",		no_argument,		NULL, 'b'},
-	{"context",		optional_argument,	NULL, 'C'},
-	{"count",		no_argument,		NULL, 'c'},
-	{"devices",		required_argument,	NULL, 'D'},
-        {"directories",		required_argument,	NULL, 'd'},
-	{"extended-regexp",	no_argument,		NULL, 'E'},
-	{"regexp",		required_argument,	NULL, 'e'},
-	{"fixed-strings",	no_argument,		NULL, 'F'},
-	{"file",		required_argument,	NULL, 'f'},
-	{"basic-regexp",	no_argument,		NULL, 'G'},
-	{"no-filename",		no_argument,		NULL, 'h'},
-	{"with-filename",	no_argument,		NULL, 'H'},
-	{"ignore-case",		no_argument,		NULL, 'i'},
-#ifndef WITHOUT_BZ2
-	{"bz2decompress",	no_argument,		NULL, 'J'},
-#endif
-	{"files-with-matches",	no_argument,		NULL, 'l'},
-	{"files-without-match", no_argument,            NULL, 'L'},
-	{"max-count",		required_argument,	NULL, 'm'},
-	{"line-number",		no_argument,		NULL, 'n'},
-	{"only-matching",	no_argument,		NULL, 'o'},
-	{"quiet",		no_argument,		NULL, 'q'},
-	{"silent",		no_argument,		NULL, 'q'},
-	{"recursive",		no_argument,		NULL, 'r'},
-	{"no-messages",		no_argument,		NULL, 's'},
-	{"binary",		no_argument,		NULL, 'U'},
-	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
-	{"invert-match",	no_argument,		NULL, 'v'},
-	{"version",		no_argument,		NULL, 'V'},
-	{"word-regexp",		no_argument,		NULL, 'w'},
-	{"line-regexp",		no_argument,		NULL, 'x'},
-	{"null",		no_argument,		NULL, 'Z'},
-	{"null-data",		no_argument,		NULL, 'z'},
-	{NULL,			no_argument,		NULL, 0}
-};
-
-/*
- * Adds a searching pattern to the internal array.
- */
-static void
-add_pattern(char *pat, size_t len)
-{
-
-	/* TODO: Check for empty patterns and shortcut */
-
-	/* Increase size if necessary */
-	if (patterns == pattern_sz) {
-		pattern_sz *= 2;
-		pattern = grep_realloc(pattern, ++pattern_sz *
-		    sizeof(*pattern));
-	}
-	if (len > 0 && pat[len - 1] == '\n')
-		--len;
-	/* pat may not be NUL-terminated */
-	pattern[patterns] = grep_malloc(len + 1);
-	memcpy(pattern[patterns], pat, len);
-	pattern[patterns][len] = '\0';
-	++patterns;
-}
-
-/*
- * Adds a file include/exclude pattern to the internal array.
- */
-static void
-add_fpattern(const char *pat, int mode)
-{
-
-	/* Increase size if necessary */
-	if (fpatterns == fpattern_sz) {
-		fpattern_sz *= 2;
-		fpattern = grep_realloc(fpattern, ++fpattern_sz *
-		    sizeof(struct epat));
-	}
-	fpattern[fpatterns].pat = grep_strdup(pat);
-	fpattern[fpatterns].mode = mode;
-	++fpatterns;
-}
-
-/*
- * Adds a directory include/exclude pattern to the internal array.
- */
-static void
-add_dpattern(const char *pat, int mode)
-{
-
-	/* Increase size if necessary */
-	if (dpatterns == dpattern_sz) {
-		dpattern_sz *= 2;
-		dpattern = grep_realloc(dpattern, ++dpattern_sz *
-		    sizeof(struct epat));
-	}
-	dpattern[dpatterns].pat = grep_strdup(pat);
-	dpattern[dpatterns].mode = mode;
-	++dpatterns;
-}
-
-/*
- * Reads searching patterns from a file and adds them with add_pattern().
- */
-static void
-read_patterns(const char *fn)
-{
-	FILE *f;
-	char *line;
-	size_t len;
-	ssize_t rlen;
-
-	if ((f = fopen(fn, "r")) == NULL)
-		err(2, "%s", fn);
-	line = NULL;
-	len = 0;
-	while ((rlen = getline(&line, &len, f)) != -1)
-		add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
-	free(line);
-	if (ferror(f))
-		err(2, "%s", fn);
-	fclose(f);
-}
-
-static inline const char *
-init_color(const char *d)
-{
-	char *c;
-
-	c = getenv("GREP_COLOR");
-	return (c != NULL ? c : d);
-}
-
-int
-main(int argc, char *argv[])
-{
-	char **aargv, **eargv, *eopts;
-	char *ep;
-	unsigned long long l;
-	unsigned int aargc, eargc, i, j;
-	int c, lastc, needpattern, newarg, prevoptind;
-
-	setlocale(LC_ALL, "");
-
-#ifndef WITHOUT_NLS
-	catalog = catopen("grep", NL_CAT_LOCALE);
-#endif
-
-	/* Check what is the program name of the binary.  In this
-	   way we can have all the funcionalities in one binary
-	   without the need of scripting and using ugly hacks. */
-	switch (__progname[0]) {
-	case 'e':
-		grepbehave = GREP_EXTENDED;
-		break;
-	case 'f':
-		grepbehave = GREP_FIXED;
-		break;
-	case 'g':
-		grepbehave = GREP_BASIC;
-		break;
-#ifndef WITHOUT_GZIP
-	case 'z':
-		filebehave = FILE_GZIP;
-		switch(__progname[1]) {
-		case 'e':
-			grepbehave = GREP_EXTENDED;
-			break;
-		case 'f':
-			grepbehave = GREP_FIXED;
-			break;
-		case 'g':
-			grepbehave = GREP_BASIC;
-			break;
-		}
-		break;
-#endif
-	}
-
-	lastc = '\0';
-	newarg = 1;
-	prevoptind = 1;
-	needpattern = 1;
-
-	eopts = getenv("GREP_OPTIONS");
-
-	/* support for extra arguments in GREP_OPTIONS */
-	eargc = 0;
-	if (eopts != NULL) {
-		char *str;
-
-		/* make an estimation of how many extra arguments we have */
-		for (j = 0; j < strlen(eopts); j++)
-			if (eopts[j] == ' ')
-				eargc++;
-
-		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
-
-		eargc = 0;
-		/* parse extra arguments */
-		while ((str = strsep(&eopts, " ")) != NULL)
-			eargv[eargc++] = grep_strdup(str);
-
-		aargv = (char **)grep_calloc(eargc + argc + 1,
-		    sizeof(char *));
-
-		aargv[0] = argv[0];
-		for (i = 0; i < eargc; i++)
-			aargv[i + 1] = eargv[i];
-		for (j = 1; j < (unsigned int)argc; j++, i++)
-			aargv[i + 1] = argv[j];
-
-		aargc = eargc + argc;
-	} else {
-		aargv = argv;
-		aargc = argc;
-	}
-
-	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
-	    -1)) {
-		switch (c) {
-		case '0': case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			if (newarg || !isdigit(lastc))
-				Aflag = 0;
-			else if (Aflag > LLONG_MAX / 10) {
-				errno = ERANGE;
-				err(2, NULL);
-			}
-			Aflag = Bflag = (Aflag * 10) + (c - '0');
-			break;
-		case 'C':
-			if (optarg == NULL) {
-				Aflag = Bflag = 2;
-				break;
-			}
-			/* FALLTHROUGH */
-		case 'A':
-			/* FALLTHROUGH */
-		case 'B':
-			errno = 0;
-			l = strtoull(optarg, &ep, 10);
-			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
-			    ((errno == EINVAL) && (l == 0)))
-				err(2, NULL);
-			else if (ep[0] != '\0') {
-				errno = EINVAL;
-				err(2, NULL);
-			}
-			if (c == 'A')
-				Aflag = l;
-			else if (c == 'B')
-				Bflag = l;
-			else
-				Aflag = Bflag = l;
-			break;
-		case 'a':
-			binbehave = BINFILE_TEXT;
-			break;
-		case 'b':
-			bflag = true;
-			break;
-		case 'c':
-			cflag = true;
-			break;
-		case 'D':
-			if (strcasecmp(optarg, "skip") == 0)
-				devbehave = DEV_SKIP;
-			else if (strcasecmp(optarg, "read") == 0)
-				devbehave = DEV_READ;
-			else
-				errx(2, getstr(3), "--devices");
-			break;
-		case 'd':
-			if (strcasecmp("recurse", optarg) == 0) {
-				Hflag = true;
-				dirbehave = DIR_RECURSE;
-			} else if (strcasecmp("skip", optarg) == 0)
-				dirbehave = DIR_SKIP;
-			else if (strcasecmp("read", optarg) == 0)
-				dirbehave = DIR_READ;
-			else
-				errx(2, getstr(3), "--directories");
-			break;
-		case 'E':
-			grepbehave = GREP_EXTENDED;
-			break;
-		case 'e':
-			add_pattern(optarg, strlen(optarg));
-			needpattern = 0;
-			break;
-		case 'F':
-			grepbehave = GREP_FIXED;
-			break;
-		case 'f':
-			read_patterns(optarg);
-			needpattern = 0;
-			break;
-		case 'G':
-			grepbehave = GREP_BASIC;
-			break;
-		case 'H':
-			Hflag = true;
-			break;
-		case 'h':
-			Hflag = false;
-			hflag = true;
-			break;
-		case 'I':
-			binbehave = BINFILE_SKIP;
-			break;
-		case 'i':
-		case 'y':
-			iflag =  true;
-			cflags |= REG_ICASE;
-			break;
-#ifndef WITHOUT_BZ2
-		case 'J':
-			filebehave = FILE_BZIP;
-			break;
-#endif
-		case 'L':
-			lflag = false;
-			Lflag = true;
-			break;
-		case 'l':
-			Lflag = false;
-			lflag = true;
-			break;
-		case 'm':
-			mflag = true;
-			errno = 0;
-			mcount = strtoull(optarg, &ep, 10);
-			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
-			    ((errno == EINVAL) && (mcount == 0)))
-				err(2, NULL);
-			else if (ep[0] != '\0') {
-				errno = EINVAL;
-				err(2, NULL);
-			}
-			break;
-		case 'n':
-			nflag = true;
-			break;
-		case 'O':
-			linkbehave = LINK_EXPLICIT;
-			break;
-		case 'o':
-			oflag = true;
-			break;
-		case 'p':
-			linkbehave = LINK_SKIP;
-			break;
-		case 'q':
-			qflag = true;
-			break;
-		case 'S':
-			linkbehave = LINK_READ;
-			break;
-		case 'R':
-		case 'r':
-			dirbehave = DIR_RECURSE;
-			Hflag = true;
-			break;
-		case 's':
-			sflag = true;
-			break;
-		case 'U':
-			binbehave = BINFILE_BIN;
-			break;
-		case 'u':
-		case MMAP_OPT:
-			/* noop, compatibility */
-			break;
-		case 'V':
-			printf(getstr(9), __progname, VERSION);
-			exit(0);
-		case 'v':
-			vflag = true;
-			break;
-		case 'w':
-			wflag = true;
-			break;
-		case 'x':
-			xflag = true;
-			break;
-		case 'Z':
-			nullflag = true;
-			break;
-		case 'z':
-			nulldataflag = true;
-			line_sep = '\0';
-			break;
-		case BIN_OPT:
-			if (strcasecmp("binary", optarg) == 0)
-				binbehave = BINFILE_BIN;
-			else if (strcasecmp("without-match", optarg) == 0)
-				binbehave = BINFILE_SKIP;
-			else if (strcasecmp("text", optarg) == 0)
-				binbehave = BINFILE_TEXT;
-			else
-				errx(2, getstr(3), "--binary-files");
-			break;
-		case COLOR_OPT:
-			color = NULL;
-			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
-			    strcasecmp("tty", optarg) == 0 ||
-			    strcasecmp("if-tty", optarg) == 0) {
-				char *term;
-
-				term = getenv("TERM");
-				if (isatty(STDOUT_FILENO) && term != NULL &&
-				    strcasecmp(term, "dumb") != 0)
-					color = init_color("01;31");
-			} else if (strcasecmp("always", optarg) == 0 ||
-			    strcasecmp("yes", optarg) == 0 ||
-			    strcasecmp("force", optarg) == 0) {
-				color = init_color("01;31");
-			} else if (strcasecmp("never", optarg) != 0 &&
-			    strcasecmp("none", optarg) != 0 &&
-			    strcasecmp("no", optarg) != 0)
-				errx(2, getstr(3), "--color");
-			break;
-#ifndef WITHOUT_GZIP
-		case DECOMPRESS_OPT:
-			filebehave = FILE_GZIP;
-			break;
-#endif
-		case LABEL_OPT:
-			label = optarg;
-			break;
-		case LINEBUF_OPT:
-			lbflag = true;
-			break;
-		case R_INCLUDE_OPT:
-			finclude = true;
-			add_fpattern(optarg, INCL_PAT);
-			break;
-		case R_EXCLUDE_OPT:
-			fexclude = true;
-			add_fpattern(optarg, EXCL_PAT);
-			break;
-		case R_DINCLUDE_OPT:
-			dinclude = true;
-			add_dpattern(optarg, INCL_PAT);
-			break;
-		case R_DEXCLUDE_OPT:
-			dexclude = true;
-			add_dpattern(optarg, EXCL_PAT);
-			break;
-		case HELP_OPT:
-		default:
-			usage();
-		}
-		lastc = c;
-		newarg = optind != prevoptind;
-		prevoptind = optind;
-	}
-	aargc -= optind;
-	aargv += optind;
-
-	/* Fail if we don't have any pattern */
-	if (aargc == 0 && needpattern)
-		usage();
-
-	/* Process patterns from command line */
-	if (aargc != 0 && needpattern) {
-		add_pattern(*aargv, strlen(*aargv));
-		--aargc;
-		++aargv;
-	}
-
-	switch (grepbehave) {
-	case GREP_FIXED:
-	case GREP_BASIC:
-		break;
-	case GREP_EXTENDED:
-		cflags |= REG_EXTENDED;
-		break;
-	default:
-		/* NOTREACHED */
-		usage();
-	}
-
-	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
-	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
-/*
- * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
- * Optimizations should be done there.
- */
-		/* Check if cheating is allowed (always is for fgrep). */
-	if (grepbehave == GREP_FIXED) {
-		for (i = 0; i < patterns; ++i)
-			fgrepcomp(&fg_pattern[i], pattern[i]);
-	} else {
-		for (i = 0; i < patterns; ++i) {
-			if (fastcomp(&fg_pattern[i], pattern[i])) {
-				/* Fall back to full regex library */
-				c = regcomp(&r_pattern[i], pattern[i], cflags);
-				if (c != 0) {
-					regerror(c, &r_pattern[i], re_error,
-					    RE_ERROR_BUF);
-					errx(2, "%s", re_error);
-				}
-			}
-		}
-	}
-
-	if (lbflag) {
-#ifdef _IOLBF
-		setvbuf(stdout, NULL, _IOLBF, 0);
-#else
-		setlinebuf(stdout);
-#endif
-	}
-
-	if ((aargc == 0 || aargc == 1) && !Hflag)
-		hflag = true;
-
-	if (aargc == 0)
-		exit(!procfile("-"));
-
-	if (dirbehave == DIR_RECURSE)
-		c = grep_tree(aargv);
-	else
-		for (c = 0; aargc--; ++aargv) {
-			if ((finclude || fexclude) && !file_matching(*aargv))
-				continue;
-			c+= procfile(*aargv);
-		}
-
-#ifndef WITHOUT_NLS
-	catclose(catalog);
-#endif
-
-	/* Find out the correct return value according to the
-	   results and the command line option. */
-	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
deleted file mode 100644
index b7ef7fa..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/grep.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/*	$NetBSD: grep.h,v 1.10 2018/08/12 09:03:21 christos Exp $	*/
-/*	$OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $	*/
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef WITHOUT_BZ2
-#include <bzlib.h>
-#endif
-#include <limits.h>
-#include <regex.h>
-#include <stdbool.h>
-#include <stdio.h>
-#ifndef WITHOUT_GZIP
-#include <zlib.h>
-#endif
-
-#ifdef WITHOUT_NLS
-#define getstr(n)	 errstr[n]
-#else
-#include <nl_types.h>
-
-extern nl_catd		 catalog;
-#define getstr(n)	 catgets(catalog, 1, n, errstr[n])
-#endif
-
-extern const char		*errstr[];
-
-#define VERSION		"2.5.1-FreeBSD"
-
-#define GREP_FIXED	0
-#define GREP_BASIC	1
-#define GREP_EXTENDED	2
-
-#define BINFILE_BIN	0
-#define BINFILE_SKIP	1
-#define BINFILE_TEXT	2
-
-#define FILE_STDIO	0
-#define FILE_GZIP	1
-#define FILE_BZIP	2
-
-#define DIR_READ	0
-#define DIR_SKIP	1
-#define DIR_RECURSE	2
-
-#define DEV_READ	0
-#define DEV_SKIP	1
-
-#define LINK_READ	0
-#define LINK_EXPLICIT	1
-#define LINK_SKIP	2
-
-#define EXCL_PAT	0
-#define INCL_PAT	1
-
-#define MAX_LINE_MATCHES	32
-
-struct file {
-	int		 fd;
-	bool		 binary;
-};
-
-struct str {
-	off_t		 off;
-	size_t		 len;
-	char		*dat;
-	char		*file;
-	int		 line_no;
-};
-
-struct epat {
-	char		*pat;
-	int		 mode;
-};
-
-typedef struct {
-	size_t		 len;
-	unsigned char	*pattern;
-	int		 qsBc[UCHAR_MAX + 1];
-	/* flags */
-	bool		 bol;
-	bool		 eol;
-	bool		 reversed;
-	bool		 word;
-} fastgrep_t;
-
-/* Flags passed to regcomp() and regexec() */
-extern int	 cflags, eflags;
-
-/* Command line flags */
-extern bool	 Eflag, Fflag, Gflag, Hflag, Lflag,
-		 bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
-		 qflag, sflag, vflag, wflag, xflag;
-extern bool	 dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag;
-extern unsigned char line_sep;
-extern unsigned long long Aflag, Bflag, mcount;
-extern char	*label;
-extern const char *color;
-extern int	 binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave;
-
-extern bool	 notfound;
-extern int	 tail;
-extern unsigned int dpatterns, fpatterns, patterns;
-extern char    **pattern;
-extern struct epat *dpattern, *fpattern;
-extern regex_t	*er_pattern, *r_pattern;
-extern fastgrep_t *fg_pattern;
-
-/* For regex errors  */
-#define RE_ERROR_BUF	512
-extern char	 re_error[RE_ERROR_BUF + 1];	/* Seems big enough */
-
-/* util.c */
-bool	 file_matching(const char *fname);
-int	 procfile(const char *fn);
-int	 grep_tree(char **argv);
-void	*grep_malloc(size_t size);
-void	*grep_calloc(size_t nmemb, size_t size);
-void	*grep_realloc(void *ptr, size_t size);
-char	*grep_strdup(const char *str);
-void	 printline(struct str *line, int sep, regmatch_t *matches, int m);
-
-/* queue.c */
-void	 enqueue(struct str *x);
-void	 printqueue(void);
-void	 clearqueue(void);
-
-/* file.c */
-void		 grep_close(struct file *f);
-struct file	*grep_open(const char *path);
-char		*grep_fgetln(struct file *f, size_t *len);
-
-/* fastgrep.c */
-int		 fastcomp(fastgrep_t *, const char *);
-void		 fgrepcomp(fastgrep_t *, const char *);
-int		 grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *);
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/queue.c b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
deleted file mode 100644
index e3c6be1..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/queue.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*	$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $	*/
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * A really poor man's queue.  It does only what it has to and gets out of
- * Dodge.  It is used in place of <sys/queue.h> to get a better performance.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $");
-
-#include <sys/param.h>
-#include <sys/queue.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "grep.h"
-
-struct qentry {
-	STAILQ_ENTRY(qentry)	list;
-	struct str	 	data;
-};
-
-static STAILQ_HEAD(, qentry)	queue = STAILQ_HEAD_INITIALIZER(queue);
-static unsigned long long	count;
-
-static struct qentry	*dequeue(void);
-
-void
-enqueue(struct str *x)
-{
-	struct qentry *item;
-
-	item = grep_malloc(sizeof(struct qentry));
-	item->data.dat = grep_malloc(sizeof(char) * x->len);
-	item->data.len = x->len;
-	item->data.line_no = x->line_no;
-	item->data.off = x->off;
-	memcpy(item->data.dat, x->dat, x->len);
-	item->data.file = x->file;
-
-	STAILQ_INSERT_TAIL(&queue, item, list);
-
-	if (++count > Bflag) {
-		item = dequeue();
-		free(item->data.dat);
-		free(item);
-	}
-}
-
-static struct qentry *
-dequeue(void)
-{
-	struct qentry *item;
-
-	item = STAILQ_FIRST(&queue);
-	if (item == NULL)
-		return (NULL);
-
-	STAILQ_REMOVE_HEAD(&queue, list);
-	--count;
-	return (item);
-}
-
-void
-printqueue(void)
-{
-	struct qentry *item;
-
-	while ((item = dequeue()) != NULL) {
-		printline(&item->data, '-', NULL, 0);
-		free(item->data.dat);
-		free(item);
-	}
-}
-
-void
-clearqueue(void)
-{
-	struct qentry *item;
-
-	while ((item = dequeue()) != NULL) {
-		free(item->data.dat);
-		free(item);
-	}
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
deleted file mode 100644
index a3c9e4c..0000000
--- a/toolbox/upstream-netbsd/usr.bin/grep/util.c
+++ /dev/null
@@ -1,500 +0,0 @@
-/*	$NetBSD: util.c,v 1.19 2018/02/05 22:14:26 mrg Exp $	*/
-/*	$FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $	*/
-/*	$OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $	*/
-
-/*-
- * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
- * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#include <sys/cdefs.h>
-__RCSID("$NetBSD: util.c,v 1.19 2018/02/05 22:14:26 mrg Exp $");
-
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <ctype.h>
-#include <err.h>
-#include <errno.h>
-#include <fnmatch.h>
-#include <fts.h>
-#include <libgen.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "grep.h"
-
-static bool	 first, first_global = true;
-static unsigned long long since_printed;
-
-static int	 procline(struct str *l, int);
-
-bool
-file_matching(const char *fname)
-{
-	char *fname_base, *fname_copy;
-	unsigned int i;
-	bool ret;
-
-	ret = finclude ? false : true;
-	fname_copy = grep_strdup(fname);
-	fname_base = basename(fname_copy);
-
-	for (i = 0; i < fpatterns; ++i) {
-		if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
-		    fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
-			if (fpattern[i].mode == EXCL_PAT) {
-				free(fname_copy);
-				return (false);
-			} else
-				ret = true;
-		}
-	}
-	free(fname_copy);
-	return (ret);
-}
-
-static inline bool
-dir_matching(const char *dname)
-{
-	unsigned int i;
-	bool ret;
-
-	ret = dinclude ? false : true;
-
-	for (i = 0; i < dpatterns; ++i) {
-		if (dname != NULL &&
-		    fnmatch(dname, dpattern[i].pat, 0) == 0) {
-			if (dpattern[i].mode == EXCL_PAT)
-				return (false);
-			else
-				ret = true;
-		}
-	}
-	return (ret);
-}
-
-/*
- * Processes a directory when a recursive search is performed with
- * the -R option.  Each appropriate file is passed to procfile().
- */
-int
-grep_tree(char **argv)
-{
-	FTS *fts;
-	FTSENT *p;
-	char *d, *dir = NULL;
-	int c, fts_flags;
-	bool ok;
-
-	c = fts_flags = 0;
-
-	switch(linkbehave) {
-	case LINK_EXPLICIT:
-		fts_flags = FTS_COMFOLLOW;
-		break;
-	case LINK_SKIP:
-		fts_flags = FTS_PHYSICAL;
-		break;
-	default:
-		fts_flags = FTS_LOGICAL;
-			
-	}
-
-	fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
-
-	if (!(fts = fts_open(argv, fts_flags, NULL)))
-		err(2, "fts_open");
-	while ((p = fts_read(fts)) != NULL) {
-		switch (p->fts_info) {
-		case FTS_DNR:
-			/* FALLTHROUGH */
-		case FTS_ERR:
-			errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno));
-			break;
-		case FTS_D:
-			/* FALLTHROUGH */
-		case FTS_DP:
-			break;
-		case FTS_DC:
-			/* Print a warning for recursive directory loop */
-			warnx("warning: %s: recursive directory loop",
-				p->fts_path);
-			break;
-		default:
-			/* Check for file exclusion/inclusion */
-			ok = true;
-			if (dexclude || dinclude) {
-				if ((d = strrchr(p->fts_path, '/')) != NULL) {
-					dir = grep_malloc(sizeof(char) *
-					    (d - p->fts_path + 1));
-					memcpy(dir, p->fts_path,
-					    d - p->fts_path);
-					dir[d - p->fts_path] = '\0';
-				}
-				ok = dir_matching(dir);
-				free(dir);
-				dir = NULL;
-			}
-			if (fexclude || finclude)
-				ok &= file_matching(p->fts_path);
-
-			if (ok)
-				c += procfile(p->fts_path);
-			break;
-		}
-	}
-
-	fts_close(fts);
-	return (c);
-}
-
-/*
- * Opens a file and processes it.  Each file is processed line-by-line
- * passing the lines to procline().
- */
-int
-procfile(const char *fn)
-{
-	struct file *f;
-	struct stat sb;
-	struct str ln;
-	mode_t s;
-	int c, t;
-
-	if (mflag && (mcount <= 0))
-		return (0);
-
-	if (strcmp(fn, "-") == 0) {
-		fn = label != NULL ? label : getstr(1);
-		f = grep_open(NULL);
-	} else {
-		if (!stat(fn, &sb)) {
-			/* Check if we need to process the file */
-			s = sb.st_mode & S_IFMT;
-			if (s == S_IFDIR && dirbehave == DIR_SKIP)
-				return (0);
-			if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
-				|| s == S_IFSOCK) && devbehave == DEV_SKIP)
-					return (0);
-		}
-		f = grep_open(fn);
-	}
-	if (f == NULL) {
-		if (!sflag)
-			warn("%s", fn);
-		if (errno == ENOENT)
-			notfound = true;
-		return (0);
-	}
-
-	ln.file = grep_malloc(strlen(fn) + 1);
-	strcpy(ln.file, fn);
-	ln.line_no = 0;
-	ln.len = 0;
-	tail = 0;
-	ln.off = -1;
-
-	for (first = true, c = 0;  c == 0 || !(lflag || qflag); ) {
-		ln.off += ln.len + 1;
-		if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0)
-			break;
-		if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep)
-			--ln.len;
-		ln.line_no++;
-
-		/* Return if we need to skip a binary file */
-		if (f->binary && binbehave == BINFILE_SKIP) {
-			grep_close(f);
-			free(ln.file);
-			free(f);
-			return (0);
-		}
-		/* Process the file line-by-line */
-		t = procline(&ln, f->binary);
-		c += t;
-
-		/* Count the matches if we have a match limit */
-		if (mflag) {
-			mcount -= t;
-			if (mcount <= 0)
-				break;
-		}
-	}
-	if (Bflag > 0)
-		clearqueue();
-	grep_close(f);
-
-	if (cflag) {
-		if (!hflag)
-			printf("%s:", ln.file);
-		printf("%u%c", c, line_sep);
-	}
-	if (lflag && !qflag && c != 0)
-		printf("%s%c", fn, line_sep);
-	if (Lflag && !qflag && c == 0)
-		printf("%s%c", fn, line_sep);
-	if (c && !cflag && !lflag && !Lflag &&
-	    binbehave == BINFILE_BIN && f->binary && !qflag)
-		printf(getstr(8), fn);
-
-	free(ln.file);
-	free(f);
-	return (c);
-}
-
-#define iswword(x)	(iswalnum((x)) || (x) == L'_')
-
-/*
- * Processes a line comparing it with the specified patterns.  Each pattern
- * is looped to be compared along with the full string, saving each and every
- * match, which is necessary to colorize the output and to count the
- * matches.  The matching lines are passed to printline() to display the
- * appropriate output.
- */
-static int
-procline(struct str *l, int nottext)
-{
-	regmatch_t matches[MAX_LINE_MATCHES];
-	regmatch_t pmatch;
-	size_t st = 0;
-	unsigned int i;
-	int c = 0, m = 0, r = 0;
-
-	/* Loop to process the whole line */
-	while (st <= l->len) {
-		pmatch.rm_so = st;
-		pmatch.rm_eo = l->len;
-
-		/* Loop to compare with all the patterns */
-		for (i = 0; i < patterns; i++) {
-/*
- * XXX: grep_search() is a workaround for speed up and should be
- * removed in the future.  See fastgrep.c.
- */
-			if (fg_pattern[i].pattern) {
-				r = grep_search(&fg_pattern[i],
-				    (unsigned char *)l->dat,
-				    l->len, &pmatch);
-				r = (r == 0) ? 0 : REG_NOMATCH;
-				st = pmatch.rm_eo;
-			} else {
-				r = regexec(&r_pattern[i], l->dat, 1,
-				    &pmatch, eflags);
-				r = (r == 0) ? 0 : REG_NOMATCH;
-				st = pmatch.rm_eo;
-			}
-			if (r == REG_NOMATCH)
-				continue;
-			/* Check for full match */
-			if (xflag &&
-			    (pmatch.rm_so != 0 ||
-			     (size_t)pmatch.rm_eo != l->len))
-				continue;
-			/* Check for whole word match */
-			if (fg_pattern[i].word && pmatch.rm_so != 0) {
-				wchar_t wbegin, wend;
-
-				wbegin = wend = L' ';
-				if (pmatch.rm_so != 0 &&
-				    sscanf(&l->dat[pmatch.rm_so - 1],
-				    "%lc", &wbegin) != 1)
-					continue;
-				if ((size_t)pmatch.rm_eo != l->len &&
-				    sscanf(&l->dat[pmatch.rm_eo],
-				    "%lc", &wend) != 1)
-					continue;
-				if (iswword(wbegin) || iswword(wend))
-					continue;
-			}
-			c = 1;
-			if (m < MAX_LINE_MATCHES)
-				matches[m++] = pmatch;
-			/* matches - skip further patterns */
-			if ((color != NULL && !oflag) || qflag || lflag)
-				break;
-		}
-
-		if (vflag) {
-			c = !c;
-			break;
-		}
-		/* One pass if we are not recording matches */
-		if ((color != NULL && !oflag) || qflag || lflag)
-			break;
-
-		if (st == (size_t)pmatch.rm_so)
-			break; 	/* No matches */
-	}
-
-	if (c && binbehave == BINFILE_BIN && nottext)
-		return (c); /* Binary file */
-
-	/* Dealing with the context */
-	if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
-		if (c) {
-			if ((Aflag || Bflag) && !first_global &&
-			    (first || since_printed > Bflag))
-				printf("--\n");
-			tail = Aflag;
-			if (Bflag > 0)
-				printqueue();
-			printline(l, ':', matches, m);
-		} else {
-			printline(l, '-', matches, m);
-			tail--;
-		}
-		first = false;
-		first_global = false;
-		since_printed = 0;
-	} else {
-		if (Bflag)
-			enqueue(l);
-		since_printed++;
-	}
-	return (c);
-}
-
-/*
- * Safe malloc() for internal use.
- */
-void *
-grep_malloc(size_t size)
-{
-	void *ptr;
-
-	if ((ptr = malloc(size)) == NULL)
-		err(2, "malloc");
-	return (ptr);
-}
-
-/*
- * Safe calloc() for internal use.
- */
-void *
-grep_calloc(size_t nmemb, size_t size)
-{
-	void *ptr;
-
-	if ((ptr = calloc(nmemb, size)) == NULL)
-		err(2, "calloc");
-	return (ptr);
-}
-
-/*
- * Safe realloc() for internal use.
- */
-void *
-grep_realloc(void *ptr, size_t size)
-{
-
-	if ((ptr = realloc(ptr, size)) == NULL)
-		err(2, "realloc");
-	return (ptr);
-}
-
-/*
- * Safe strdup() for internal use.
- */
-char *
-grep_strdup(const char *str)
-{
-	char *ret;
-
-	if ((ret = strdup(str)) == NULL)
-		err(2, "strdup");
-	return (ret);
-}
-
-/*
- * Prints a matching line according to the command line options.
- */
-void
-printline(struct str *line, int sep, regmatch_t *matches, int m)
-{
-	size_t a = 0;
-	int i, n = 0;
-
-	if (!hflag) {
-		if (nullflag == 0)
-			fputs(line->file, stdout);
-		else {
-			printf("%s", line->file);
-			putchar(0);
-		}
-		++n;
-	}
-	if (nflag) {
-		if (n > 0)
-			putchar(sep);
-		printf("%d", line->line_no);
-		++n;
-	}
-	if (bflag) {
-		if (n > 0)
-			putchar(sep);
-		printf("%lld", (long long)line->off);
-		++n;
-	}
-	if (n)
-		putchar(sep);
-	/* --color and -o */
-	if ((oflag || color) && m > 0) {
-		for (i = 0; i < m; i++) {
-			if (!oflag)
-				fwrite(line->dat + a, matches[i].rm_so - a, 1,
-				    stdout);
-			if (color) 
-				fprintf(stdout, "\33[%sm\33[K", color);
-
-			fwrite(line->dat + matches[i].rm_so, 
-			    matches[i].rm_eo - matches[i].rm_so, 1,
-			    stdout);
-
-			if (color) 
-				fprintf(stdout, "\33[m\33[K");
-			a = matches[i].rm_eo;
-			if (oflag)
-				putchar('\n');
-		}
-		if (!oflag) {
-			if (line->len - a > 0)
-				fwrite(line->dat + a, line->len - a, 1, stdout);
-			putchar(line_sep);
-		}
-	} else {
-		fwrite(line->dat, line->len, 1, stdout);
-		putchar(line_sep);
-	}
-}
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index 65b271a..1666cfb 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -1,4 +1,3 @@
-//
 // Copyright (C) 2015 The Android Open-Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,14 +19,15 @@
 // to only building on ARM if they include assembly. Individual makefiles
 // are responsible for having their own logic, for fine-grained control.
 
-cc_library_shared {
-    name: "gatekeeper.trusty",
+cc_binary {
+    name: "android.hardware.gatekeeper@1.0-service.trusty",
+    defaults: ["hidl_defaults"],
     vendor: true,
-
     relative_install_path: "hw",
+    init_rc: ["android.hardware.gatekeeper@1.0-service.trusty.rc"],
 
     srcs: [
-        "module.cpp",
+        "service.cpp",
         "trusty_gatekeeper_ipc.c",
         "trusty_gatekeeper.cpp",
     ],
@@ -39,10 +39,16 @@
     ],
 
     shared_libs: [
+        "android.hardware.gatekeeper@1.0",
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
         "libgatekeeper",
+        "libutils",
         "liblog",
         "libcutils",
         "libtrusty",
     ],
-    header_libs: ["libhardware_headers"],
+
+    vintf_fragments: ["android.hardware.gatekeeper@1.0-service.trusty.xml"],
 }
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
new file mode 100644
index 0000000..5413a6c
--- /dev/null
+++ b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.gatekeeper-1-0 /vendor/bin/hw/android.hardware.gatekeeper@1.0-service.trusty
+    class hal
+    user system
+    group system
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
new file mode 100644
index 0000000..19714a8
--- /dev/null
+++ b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.gatekeeper</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+        <name>IGatekeeper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/trusty/gatekeeper/module.cpp b/trusty/gatekeeper/module.cpp
deleted file mode 100644
index 0ee3c2f..0000000
--- a/trusty/gatekeeper/module.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <hardware/hardware.h>
-
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-
-#include "trusty_gatekeeper.h"
-
-using gatekeeper::TrustyGateKeeperDevice;
-
-static int trusty_gatekeeper_open(const hw_module_t *module, const char *name,
-        hw_device_t **device) {
-
-    if (strcmp(name, HARDWARE_GATEKEEPER) != 0) {
-        return -EINVAL;
-    }
-
-    TrustyGateKeeperDevice *gatekeeper = new TrustyGateKeeperDevice(module);
-    if (gatekeeper == NULL) return -ENOMEM;
-    *device = gatekeeper->hw_device();
-
-    return 0;
-}
-
-static struct hw_module_methods_t gatekeeper_module_methods = {
-    .open = trusty_gatekeeper_open,
-};
-
-struct gatekeeper_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
-    .common = {
-        .tag = HARDWARE_MODULE_TAG,
-        .module_api_version = GATEKEEPER_MODULE_API_VERSION_0_1,
-        .hal_api_version = HARDWARE_HAL_API_VERSION,
-        .id = GATEKEEPER_HARDWARE_MODULE_ID,
-        .name = "Trusty GateKeeper HAL",
-        .author = "The Android Open Source Project",
-        .methods = &gatekeeper_module_methods,
-        .dso = 0,
-        .reserved = {}
-    },
-};
diff --git a/trusty/gatekeeper/service.cpp b/trusty/gatekeeper/service.cpp
new file mode 100644
index 0000000..c5ee488
--- /dev/null
+++ b/trusty/gatekeeper/service.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "android.hardware.gatekeeper@1.0-service.trusty"
+
+#include <android-base/logging.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+#include <hidl/LegacySupport.h>
+
+#include "trusty_gatekeeper.h"
+
+// Generated HIDL files
+using android::hardware::gatekeeper::V1_0::IGatekeeper;
+using gatekeeper::TrustyGateKeeperDevice;
+
+int main() {
+    ::android::hardware::configureRpcThreadpool(1, true /* willJoinThreadpool */);
+    android::sp<TrustyGateKeeperDevice> gatekeeper(new TrustyGateKeeperDevice());
+    auto status = gatekeeper->registerAsService();
+    if (status != android::OK) {
+        LOG(FATAL) << "Could not register service for Gatekeeper 1.0 (trusty) (" << status << ")";
+    }
+
+    android::hardware::joinRpcThreadpool();
+    return -1;  // Should never get here.
+}
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index b3fbfa9..d149664 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -16,147 +16,131 @@
 
 #define LOG_TAG "TrustyGateKeeper"
 
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include <type_traits>
-
-#include <log/log.h>
+#include <android-base/logging.h>
+#include <limits>
 
 #include "trusty_gatekeeper.h"
 #include "trusty_gatekeeper_ipc.h"
 #include "gatekeeper_ipc.h"
 
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
+using ::gatekeeper::EnrollRequest;
+using ::gatekeeper::EnrollResponse;
+using ::gatekeeper::ERROR_INVALID;
+using ::gatekeeper::ERROR_MEMORY_ALLOCATION_FAILED;
+using ::gatekeeper::ERROR_NONE;
+using ::gatekeeper::ERROR_RETRY;
+using ::gatekeeper::SizedBuffer;
+using ::gatekeeper::VerifyRequest;
+using ::gatekeeper::VerifyResponse;
+
 namespace gatekeeper {
 
-const uint32_t SEND_BUF_SIZE = 8192;
-const uint32_t RECV_BUF_SIZE = 8192;
+constexpr const uint32_t SEND_BUF_SIZE = 8192;
+constexpr const uint32_t RECV_BUF_SIZE = 8192;
 
-TrustyGateKeeperDevice::TrustyGateKeeperDevice(const hw_module_t *module) {
-#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
-    static_assert(std::is_standard_layout<TrustyGateKeeperDevice>::value,
-                  "TrustyGateKeeperDevice must be standard layout");
-    static_assert(offsetof(TrustyGateKeeperDevice, device_) == 0,
-                  "device_ must be the first member of TrustyGateKeeperDevice");
-    static_assert(offsetof(TrustyGateKeeperDevice, device_.common) == 0,
-                  "common must be the first member of gatekeeper_device");
-#else
-    assert(reinterpret_cast<gatekeeper_device_t *>(this) == &device_);
-    assert(reinterpret_cast<hw_device_t *>(this) == &(device_.common));
-#endif
-
-    memset(&device_, 0, sizeof(device_));
-    device_.common.tag = HARDWARE_DEVICE_TAG;
-    device_.common.version = 1;
-    device_.common.module = const_cast<hw_module_t *>(module);
-    device_.common.close = close_device;
-
-    device_.enroll = enroll;
-    device_.verify = verify;
-    device_.delete_user = nullptr;
-    device_.delete_all_users = nullptr;
-
+TrustyGateKeeperDevice::TrustyGateKeeperDevice() {
     int rc = trusty_gatekeeper_connect();
     if (rc < 0) {
-        ALOGE("Error initializing trusty session: %d", rc);
+        LOG(ERROR) << "Error initializing trusty session: " << rc;
     }
 
     error_ = rc;
-
-}
-
-hw_device_t* TrustyGateKeeperDevice::hw_device() {
-    return &device_.common;
-}
-
-int TrustyGateKeeperDevice::close_device(hw_device_t* dev) {
-    delete reinterpret_cast<TrustyGateKeeperDevice *>(dev);
-    return 0;
 }
 
 TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
     trusty_gatekeeper_disconnect();
 }
 
-int TrustyGateKeeperDevice::Enroll(uint32_t uid, const uint8_t *current_password_handle,
-        uint32_t current_password_handle_length, const uint8_t *current_password,
-        uint32_t current_password_length, const uint8_t *desired_password,
-        uint32_t desired_password_length, uint8_t **enrolled_password_handle,
-        uint32_t *enrolled_password_handle_length) {
-
-    if (error_ != 0) {
-        return error_;
-    }
-
-    SizedBuffer desired_password_buffer(desired_password_length);
-    memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
-
-    SizedBuffer current_password_handle_buffer(current_password_handle_length);
-    if (current_password_handle) {
-        memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
-                current_password_handle_length);
-    }
-
-    SizedBuffer current_password_buffer(current_password_length);
-    if (current_password) {
-        memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
-    }
-
-    EnrollRequest request(uid, &current_password_handle_buffer, &desired_password_buffer,
-            &current_password_buffer);
-    EnrollResponse response;
-
-    gatekeeper_error_t error = Send(request, &response);
-
-    if (error == ERROR_RETRY) {
-        return response.retry_timeout;
-    } else if (error != ERROR_NONE) {
-        return -EINVAL;
-    }
-
-    *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
-    *enrolled_password_handle_length = response.enrolled_password_handle.length;
-
-
-    return 0;
+SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
+    if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
+    auto dummy = new uint8_t[vec.size()];
+    std::copy(vec.begin(), vec.end(), dummy);
+    return {dummy, static_cast<uint32_t>(vec.size())};
 }
 
-int TrustyGateKeeperDevice::Verify(uint32_t uid, uint64_t challenge,
-        const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-        const uint8_t *provided_password, uint32_t provided_password_length,
-        uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+Return<void> TrustyGateKeeperDevice::enroll(uint32_t uid,
+                                            const hidl_vec<uint8_t>& currentPasswordHandle,
+                                            const hidl_vec<uint8_t>& currentPassword,
+                                            const hidl_vec<uint8_t>& desiredPassword,
+                                            enroll_cb _hidl_cb) {
     if (error_ != 0) {
-        return error_;
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return {};
     }
 
-    SizedBuffer password_handle_buffer(enrolled_password_handle_length);
-    memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
-            enrolled_password_handle_length);
-    SizedBuffer provided_password_buffer(provided_password_length);
-    memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
+    if (desiredPassword.size() == 0) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return {};
+    }
 
-    VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
+    EnrollRequest request(uid, hidl_vec2sized_buffer(currentPasswordHandle),
+                          hidl_vec2sized_buffer(desiredPassword),
+                          hidl_vec2sized_buffer(currentPassword));
+    EnrollResponse response;
+    auto error = Send(request, &response);
+    if (error != ERROR_NONE) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+    } else if (response.error == ERROR_RETRY) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+    } else if (response.error != ERROR_NONE) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+    } else {
+        hidl_vec<uint8_t> new_handle(response.enrolled_password_handle.Data<uint8_t>(),
+                                     response.enrolled_password_handle.Data<uint8_t>() +
+                                             response.enrolled_password_handle.size());
+        _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, new_handle});
+    }
+    return {};
+}
+
+Return<void> TrustyGateKeeperDevice::verify(
+        uint32_t uid, uint64_t challenge,
+        const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
+        const ::android::hardware::hidl_vec<uint8_t>& providedPassword, verify_cb _hidl_cb) {
+    if (error_ != 0) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return {};
+    }
+
+    if (enrolledPasswordHandle.size() == 0) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return {};
+    }
+
+    VerifyRequest request(uid, challenge, hidl_vec2sized_buffer(enrolledPasswordHandle),
+                          hidl_vec2sized_buffer(providedPassword));
     VerifyResponse response;
 
-    gatekeeper_error_t error = Send(request, &response);
+    auto error = Send(request, &response);
+    if (error != ERROR_NONE) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+    } else if (response.error == ERROR_RETRY) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+    } else if (response.error != ERROR_NONE) {
+        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+    } else {
+        hidl_vec<uint8_t> auth_token(
+                response.auth_token.Data<uint8_t>(),
+                response.auth_token.Data<uint8_t>() + response.auth_token.size());
 
-    if (error == ERROR_RETRY) {
-        return response.retry_timeout;
-    } else if (error != ERROR_NONE) {
-        return -EINVAL;
+        _hidl_cb({response.request_reenroll ? GatekeeperStatusCode::STATUS_REENROLL
+                                            : GatekeeperStatusCode::STATUS_OK,
+                  response.retry_timeout, auth_token});
     }
+    return {};
+}
 
-    if (auth_token != NULL && auth_token_length != NULL) {
-       *auth_token = response.auth_token.buffer.release();
-       *auth_token_length = response.auth_token.length;
-    }
+Return<void> TrustyGateKeeperDevice::deleteUser(uint32_t /*uid*/, deleteUser_cb _hidl_cb) {
+    _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+    return {};
+}
 
-    if (request_reenroll != NULL) {
-        *request_reenroll = response.request_reenroll;
-    }
-
-    return 0;
+Return<void> TrustyGateKeeperDevice::deleteAllUsers(deleteAllUsers_cb _hidl_cb) {
+    _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+    return {};
 }
 
 gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
@@ -172,7 +156,7 @@
     uint32_t response_size = RECV_BUF_SIZE;
     int rc = trusty_gatekeeper_call(command, send_buf, request_size, recv_buf, &response_size);
     if (rc < 0) {
-        ALOGE("error (%d) calling gatekeeper TA", rc);
+        LOG(ERROR) << "error (" << rc << ") calling gatekeeper TA";
         return ERROR_INVALID;
     }
 
@@ -182,51 +166,4 @@
     return response->Deserialize(payload, payload + response_size);
 }
 
-static inline TrustyGateKeeperDevice *convert_device(const gatekeeper_device *dev) {
-    return reinterpret_cast<TrustyGateKeeperDevice *>(const_cast<gatekeeper_device *>(dev));
-}
-
-/* static */
-int TrustyGateKeeperDevice::enroll(const struct gatekeeper_device *dev, uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
-
-    if (dev == NULL ||
-            enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
-            desired_password == NULL || desired_password_length == 0)
-        return -EINVAL;
-
-    // Current password and current password handle go together
-    if (current_password_handle == NULL || current_password_handle_length == 0 ||
-            current_password == NULL || current_password_length == 0) {
-        current_password_handle = NULL;
-        current_password_handle_length = 0;
-        current_password = NULL;
-        current_password_length = 0;
-    }
-
-    return convert_device(dev)->Enroll(uid, current_password_handle, current_password_handle_length,
-            current_password, current_password_length, desired_password, desired_password_length,
-            enrolled_password_handle, enrolled_password_handle_length);
-
-}
-
-/* static */
-int TrustyGateKeeperDevice::verify(const struct gatekeeper_device *dev, uint32_t uid,
-        uint64_t challenge, const uint8_t *enrolled_password_handle,
-        uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
-        uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
-        bool *request_reenroll) {
-
-    if (dev == NULL || enrolled_password_handle == NULL ||
-            provided_password == NULL) {
-        return -EINVAL;
-    }
-
-    return convert_device(dev)->Verify(uid, challenge, enrolled_password_handle,
-            enrolled_password_handle_length, provided_password, provided_password_length,
-            auth_token, auth_token_length, request_reenroll);
-}
 };
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
index 2becc49..c0713f4 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.h
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -17,84 +17,34 @@
 #ifndef TRUSTY_GATEKEEPER_H
 #define TRUSTY_GATEKEEPER_H
 
-#include <hardware/gatekeeper.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#include <hidl/Status.h>
+
+#include <memory>
+
 #include <gatekeeper/gatekeeper_messages.h>
 
 #include "gatekeeper_ipc.h"
 
 namespace gatekeeper {
 
-class TrustyGateKeeperDevice {
-    public:
-
-    explicit TrustyGateKeeperDevice(const hw_module_t* module);
+class TrustyGateKeeperDevice : public ::android::hardware::gatekeeper::V1_0::IGatekeeper {
+  public:
+    explicit TrustyGateKeeperDevice();
     ~TrustyGateKeeperDevice();
-
-    hw_device_t* hw_device();
-
     /**
      * Enrolls password_payload, which should be derived from a user selected pin or password,
      * with the authentication factor private key used only for enrolling authentication
      * factor data.
      *
      * Returns: 0 on success or an error code less than 0 on error.
-     * On error, enrolled_password will not be allocated.
-     */
-    int Enroll(uint32_t uid, const uint8_t *current_password_handle,
-            uint32_t current_password_handle_length, const uint8_t *current_password,
-            uint32_t current_password_length, const uint8_t *desired_password,
-            uint32_t desired_password_length, uint8_t **enrolled_password_handle,
-            uint32_t *enrolled_password_handle_length);
-
-    /**
-     * Verifies provided_password matches expected_password after enrolling
-     * with the authentication factor private key.
-     *
-     * Implementations of this module may retain the result of this call
-     * to attest to the recency of authentication.
-     *
-     * On success, writes the address of a verification token to verification_token,
-     *
-     * Returns: 0 on success or an error code less than 0 on error
-     * On error, verification token will not be allocated
-     */
-    int Verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle,
-            uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
-            uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
-            bool *request_reenroll);
-
-    private:
-
-    gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
-                           GateKeeperMessage* response);
-
-    gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
-        return Send(GK_ENROLL, request, response);
-    }
-
-    gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
-        return Send(GK_VERIFY, request, response);
-    }
-
-    // Static methods interfacing the HAL API with the TrustyGateKeeper device
-
-    /**
-     * Enrolls desired_password, which should be derived from a user selected pin or password,
-     * with the authentication factor private key used only for enrolling authentication
-     * factor data.
-     *
-     * If there was already a password enrolled, it should be provided in
-     * current_password_handle, along with the current password in current_password
-     * that should validate against current_password_handle.
-     *
-     * Returns: 0 on success or an error code less than 0 on error.
      * On error, enrolled_password_handle will not be allocated.
      */
-    static int enroll(const struct gatekeeper_device *dev, uint32_t uid,
-            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
-            const uint8_t *current_password, uint32_t current_password_length,
-            const uint8_t *desired_password, uint32_t desired_password_length,
-            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
+    ::android::hardware::Return<void> enroll(
+            uint32_t uid, const ::android::hardware::hidl_vec<uint8_t>& currentPasswordHandle,
+            const ::android::hardware::hidl_vec<uint8_t>& currentPassword,
+            const ::android::hardware::hidl_vec<uint8_t>& desiredPassword,
+            enroll_cb _hidl_cb) override;
 
     /**
      * Verifies provided_password matches enrolled_password_handle.
@@ -109,18 +59,32 @@
      * Returns: 0 on success or an error code less than 0 on error
      * On error, verification token will not be allocated
      */
-    static int verify(const struct gatekeeper_device *dev, uint32_t uid, uint64_t challenge,
-            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
-            const uint8_t *provided_password, uint32_t provided_password_length,
-            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
+    ::android::hardware::Return<void> verify(
+            uint32_t uid, uint64_t challenge,
+            const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
+            const ::android::hardware::hidl_vec<uint8_t>& providedPassword,
+            verify_cb _hidl_cb) override;
 
-    static int close_device(hw_device_t* dev);
+    ::android::hardware::Return<void> deleteUser(uint32_t uid, deleteUser_cb _hidl_cb) override;
 
-    gatekeeper_device device_;
+    ::android::hardware::Return<void> deleteAllUsers(deleteAllUsers_cb _hidl_cb) override;
+
+  private:
+    gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
+                           GateKeeperMessage* response);
+
+    gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
+        return Send(GK_ENROLL, request, response);
+    }
+
+    gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
+        return Send(GK_VERIFY, request, response);
+    }
+
     int error_;
-
 };
-}
+
+}  // namespace gatekeeper
 
 #endif
 
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 445d3ce..fd8daa8 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -24,9 +24,7 @@
 
 PRODUCT_PACKAGES += \
 	android.hardware.keymaster@4.0-service.trusty \
-	android.hardware.gatekeeper@1.0-service \
-	android.hardware.gatekeeper@1.0-impl \
-	gatekeeper.trusty
+	android.hardware.gatekeeper@1.0-service.trusty
 
 PRODUCT_PROPERTY_OVERRIDES += \
 	ro.hardware.keystore=trusty \