Merge "init: start apexd as early as we can."
diff --git a/adb/Android.bp b/adb/Android.bp
index 41e752f..bccc71a 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -138,8 +138,12 @@
         "client/usb_libusb.cpp",
         "client/usb_dispatch.cpp",
         "client/transport_mdns.cpp",
+        "client/fastdeploy.cpp",
+        "client/fastdeploycallbacks.cpp",
     ],
 
+    generated_headers: ["platform_tools_version"],
+
     target: {
         linux: {
             srcs: ["client/usb_linux.cpp"],
@@ -237,6 +241,7 @@
         "client/file_sync_client.cpp",
         "client/main.cpp",
         "client/console.cpp",
+        "client/adb_install.cpp",
         "client/line_printer.cpp",
         "shell_service_protocol.cpp",
     ],
@@ -260,6 +265,10 @@
     // will violate ODR
     shared_libs: [],
 
+    required: [
+        "deploypatchgenerator",
+    ],
+
     target: {
         darwin: {
             cflags: [
@@ -296,6 +305,8 @@
         "daemon/include",
     ],
 
+    generated_headers: ["platform_tools_version"],
+
     static_libs: [
         "libdiagnose_usb",
         "libqemu_pipe",
@@ -338,10 +349,6 @@
         "libavb_user",
         "libdiagnose_usb",
         "libqemu_pipe",
-
-        // `daemon/shell_service.cpp` uses selinux_android_setcon(), which is not exposed by
-        // libselinux.
-        "libselinux",
     ],
 
     shared_libs: [
@@ -356,6 +363,7 @@
         "libfs_mgr",
         "liblog",
         "libmdnssd",
+        "libselinux",
     ],
 }
 
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 8e028f4..62e8908 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -45,6 +45,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <build/version.h>
+#include <platform_tools_version.h>
 
 #include "adb_auth.h"
 #include "adb_io.h"
@@ -65,10 +66,11 @@
     // Don't change the format of this --- it's parsed by ddmlib.
     return android::base::StringPrintf(
         "Android Debug Bridge version %d.%d.%d\n"
-        "Version %s\n"
+        "Version %s-%s\n"
         "Installed as %s\n",
         ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
-        android::build::GetBuildNumber().c_str(), android::base::GetExecutablePath().c_str());
+        PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str(),
+        android::base::GetExecutablePath().c_str());
 }
 
 void fatal(const char *fmt, ...) {
@@ -631,11 +633,11 @@
     fprintf(stderr, "Full server startup log: %s\n", GetLogFilePath().c_str());
     fprintf(stderr, "Server had pid: %d\n", pid);
 
-    unique_fd fd(adb_open(GetLogFilePath().c_str(), O_RDONLY));
+    android::base::unique_fd fd(unix_open(GetLogFilePath().c_str(), O_RDONLY));
     if (fd == -1) return;
 
     // Let's not show more than 128KiB of log...
-    adb_lseek(fd, -128 * 1024, SEEK_END);
+    unix_lseek(fd, -128 * 1024, SEEK_END);
     std::string content;
     if (!android::base::ReadFdToString(fd, &content)) return;
 
@@ -825,7 +827,7 @@
                 memcmp(temp, expected, expected_length) == 0) {
                 got_ack = true;
             } else {
-                ReportServerStartupFailure(GetProcessId(process_handle.get()));
+                ReportServerStartupFailure(pinfo.dwProcessId);
                 return -1;
             }
         } else {
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index ffac315..35017f0 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -49,9 +49,9 @@
 
 
 #if defined(_WIN32)
-constexpr char kNullFileName[] = "NUL";
+static constexpr char kNullFileName[] = "NUL";
 #else
-constexpr char kNullFileName[] = "/dev/null";
+static constexpr char kNullFileName[] = "/dev/null";
 #endif
 
 void close_stdin() {
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f764a0e..f6ce8e2 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -24,7 +24,7 @@
 
 #include <android-base/macros.h>
 
-int syntax_error(const char*, ...);
+int syntax_error(const char*, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 void close_stdin();
 
diff --git a/adb/client/adb_client.cpp b/adb/client/adb_client.cpp
index 1959258..eda4b77 100644
--- a/adb/client/adb_client.cpp
+++ b/adb/client/adb_client.cpp
@@ -185,6 +185,11 @@
         return false;
     }
 
+    // The server might send OKAY, so consume that.
+    char buf[4];
+    ReadFdExactly(fd, buf, 4);
+    // Now that no more data is expected, wait for socket orderly shutdown or error, indicating
+    // server death.
     ReadOrderlyShutdown(fd);
     return true;
 }
diff --git a/adb/client/adb_client.h b/adb/client/adb_client.h
index fca435e..d467539 100644
--- a/adb/client/adb_client.h
+++ b/adb/client/adb_client.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _ADB_CLIENT_H_
-#define _ADB_CLIENT_H_
+#pragma once
 
 #include "adb.h"
 #include "sysdeps.h"
@@ -63,5 +62,3 @@
 
 // Get the feature set of the current preferred transport.
 bool adb_get_feature_set(FeatureSet* _Nonnull feature_set, std::string* _Nonnull error);
-
-#endif
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
new file mode 100644
index 0000000..c00d955
--- /dev/null
+++ b/adb/client/adb_install.cpp
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG ADB
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "adb.h"
+#include "adb_client.h"
+#include "adb_install.h"
+#include "adb_utils.h"
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "fastdeploy.h"
+#include "sysdeps.h"
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+static constexpr int kFastDeployMinApi = 24;
+
+static bool _use_legacy_install() {
+    FeatureSet features;
+    std::string error;
+    if (!adb_get_feature_set(&features, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return true;
+    }
+    return !CanUseFeature(features, kFeatureCmd);
+}
+
+static int pm_command(int argc, const char** argv) {
+    std::string cmd = "pm";
+
+    while (argc-- > 0) {
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(cmd);
+}
+
+static int uninstall_app_streamed(int argc, const char** argv) {
+    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
+    std::string cmd = "cmd package";
+    while (argc-- > 0) {
+        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
+        if (strcmp(*argv, "-k") == 0) {
+            printf("The -k option uninstalls the application while retaining the "
+                   "data/cache.\n"
+                   "At the moment, there is no way to remove the remaining data.\n"
+                   "You will have to reinstall the application with the same "
+                   "signature, and fully "
+                   "uninstall it.\n"
+                   "If you truly wish to continue, execute 'adb shell cmd package "
+                   "uninstall -k'.\n");
+            return EXIT_FAILURE;
+        }
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(cmd);
+}
+
+static int uninstall_app_legacy(int argc, const char** argv) {
+    /* if the user choose the -k option, we refuse to do it until devices are
+       out with the option to uninstall the remaining data somehow (adb/ui) */
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-k")) {
+            printf("The -k option uninstalls the application while retaining the "
+                   "data/cache.\n"
+                   "At the moment, there is no way to remove the remaining data.\n"
+                   "You will have to reinstall the application with the same "
+                   "signature, and fully "
+                   "uninstall it.\n"
+                   "If you truly wish to continue, execute 'adb shell pm uninstall "
+                   "-k'\n.");
+            return EXIT_FAILURE;
+        }
+    }
+
+    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+    return pm_command(argc, argv);
+}
+
+int uninstall_app(int argc, const char** argv) {
+    if (_use_legacy_install()) {
+        return uninstall_app_legacy(argc, argv);
+    }
+    return uninstall_app_streamed(argc, argv);
+}
+
+static void read_status_line(int fd, char* buf, size_t count) {
+    count--;
+    while (count > 0) {
+        int len = adb_read(fd, buf, count);
+        if (len <= 0) {
+            break;
+        }
+
+        buf += len;
+        count -= len;
+    }
+    *buf = '\0';
+}
+
+static int delete_device_patch_file(const char* apkPath) {
+    std::string patchDevicePath = get_patch_path(apkPath);
+    return delete_device_file(patchDevicePath);
+}
+
+static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
+                                bool use_localagent) {
+    printf("Performing Streamed Install\n");
+
+    // The last argument must be the APK file
+    const char* file = argv[argc - 1];
+    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
+        return syntax_error("filename doesn't end .apk: %s", file);
+    }
+
+    if (use_fastdeploy == true) {
+        TemporaryFile metadataTmpFile;
+        TemporaryFile patchTmpFile;
+
+        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
+        int metadata_len = extract_metadata(file, metadataFile);
+        fclose(metadataFile);
+
+        int result = -1;
+        if (metadata_len <= 0) {
+            printf("failed to extract metadata %d\n", metadata_len);
+            return 1;
+        } else {
+            int create_patch_result = create_patch(file, metadataTmpFile.path, patchTmpFile.path);
+            if (create_patch_result != 0) {
+                printf("Patch creation failure, error code: %d\n", create_patch_result);
+                result = create_patch_result;
+                goto cleanup_streamed_apk;
+            } else {
+                std::vector<const char*> pm_args;
+                // pass all but 1st (command) and last (apk path) parameters through to pm for
+                // session creation
+                for (int i = 1; i < argc - 1; i++) {
+                    pm_args.push_back(argv[i]);
+                }
+                int apply_patch_result =
+                        install_patch(file, patchTmpFile.path, pm_args.size(), pm_args.data());
+                if (apply_patch_result != 0) {
+                    printf("Patch application failure, error code: %d\n", apply_patch_result);
+                    result = apply_patch_result;
+                    goto cleanup_streamed_apk;
+                }
+            }
+        }
+
+    cleanup_streamed_apk:
+        if (use_fastdeploy == true) {
+            delete_device_patch_file(file);
+        }
+        return result;
+    } else {
+        struct stat sb;
+        if (stat(file, &sb) == -1) {
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            return 1;
+        }
+
+        int localFd = adb_open(file, O_RDONLY);
+        if (localFd < 0) {
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            return 1;
+        }
+
+        std::string error;
+        std::string cmd = "exec:cmd package";
+
+        // don't copy the APK name, but, copy the rest of the arguments as-is
+        while (argc-- > 1) {
+            cmd += " " + escape_arg(std::string(*argv++));
+        }
+
+        // add size parameter [required for streaming installs]
+        // do last to override any user specified value
+        cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+
+        int remoteFd = adb_connect(cmd, &error);
+        if (remoteFd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            adb_close(localFd);
+            return 1;
+        }
+
+        char buf[BUFSIZ];
+        copy_to_file(localFd, remoteFd);
+        read_status_line(remoteFd, buf, sizeof(buf));
+
+        adb_close(localFd);
+        adb_close(remoteFd);
+
+        if (!strncmp("Success", buf, 7)) {
+            fputs(buf, stdout);
+            return 0;
+        }
+        fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+        return 1;
+    }
+}
+
+static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
+                              bool use_localagent) {
+    static const char* const DATA_DEST = "/data/local/tmp/%s";
+    static const char* const SD_DEST = "/sdcard/tmp/%s";
+    const char* where = DATA_DEST;
+
+    printf("Performing Push Install\n");
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-s")) {
+            where = SD_DEST;
+        }
+    }
+
+    // Find last APK argument.
+    // All other arguments passed through verbatim.
+    int last_apk = -1;
+    for (int i = argc - 1; i >= 0; i--) {
+        if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
+            last_apk = i;
+            break;
+        }
+    }
+
+    if (last_apk == -1) return syntax_error("need APK file on command line");
+
+    int result = -1;
+    std::vector<const char*> apk_file = {argv[last_apk]};
+    std::string apk_dest =
+            android::base::StringPrintf(where, android::base::Basename(argv[last_apk]).c_str());
+
+    if (use_fastdeploy == true) {
+        TemporaryFile metadataTmpFile;
+        TemporaryFile patchTmpFile;
+
+        FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
+        int metadata_len = extract_metadata(apk_file[0], metadataFile);
+        fclose(metadataFile);
+
+        if (metadata_len <= 0) {
+            printf("failed to extract metadata %d\n", metadata_len);
+            return 1;
+        } else {
+            int create_patch_result =
+                    create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
+            if (create_patch_result != 0) {
+                printf("Patch creation failure, error code: %d\n", create_patch_result);
+                result = create_patch_result;
+                goto cleanup_apk;
+            } else {
+                int apply_patch_result =
+                        apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
+                if (apply_patch_result != 0) {
+                    printf("Patch application failure, error code: %d\n", apply_patch_result);
+                    result = apply_patch_result;
+                    goto cleanup_apk;
+                }
+            }
+        }
+    } else {
+        if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
+    }
+
+    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+    result = pm_command(argc, argv);
+
+cleanup_apk:
+    if (use_fastdeploy == true) {
+        delete_device_patch_file(apk_file[0]);
+    }
+    delete_device_file(apk_dest);
+    return result;
+}
+
+int install_app(int argc, const char** argv) {
+    std::vector<int> processedArgIndicies;
+    enum installMode {
+        INSTALL_DEFAULT,
+        INSTALL_PUSH,
+        INSTALL_STREAM
+    } installMode = INSTALL_DEFAULT;
+    bool use_fastdeploy = false;
+    bool is_reinstall = false;
+    bool use_localagent = false;
+    FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "--streaming")) {
+            processedArgIndicies.push_back(i);
+            installMode = INSTALL_STREAM;
+        } else if (!strcmp(argv[i], "--no-streaming")) {
+            processedArgIndicies.push_back(i);
+            installMode = INSTALL_PUSH;
+        } else if (!strcmp(argv[i], "-r")) {
+            // Note that this argument is not added to processedArgIndicies because it
+            // must be passed through to pm
+            is_reinstall = true;
+        } else if (!strcmp(argv[i], "--fastdeploy")) {
+            processedArgIndicies.push_back(i);
+            use_fastdeploy = true;
+        } else if (!strcmp(argv[i], "--no-fastdeploy")) {
+            processedArgIndicies.push_back(i);
+            use_fastdeploy = false;
+        } else if (!strcmp(argv[i], "--force-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateAlways;
+        } else if (!strcmp(argv[i], "--date-check-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
+        } else if (!strcmp(argv[i], "--version-check-agent")) {
+            processedArgIndicies.push_back(i);
+            agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
+#ifndef _WIN32
+        } else if (!strcmp(argv[i], "--local-agent")) {
+            processedArgIndicies.push_back(i);
+            use_localagent = true;
+#endif
+        }
+        // TODO: --installlog <filename>
+    }
+
+    if (installMode == INSTALL_DEFAULT) {
+        if (_use_legacy_install()) {
+            installMode = INSTALL_PUSH;
+        } else {
+            installMode = INSTALL_STREAM;
+        }
+    }
+
+    if (installMode == INSTALL_STREAM && _use_legacy_install() == true) {
+        return syntax_error("Attempting to use streaming install on unsupported deivce.");
+    }
+
+    if (use_fastdeploy == true && is_reinstall == false) {
+        printf("Fast Deploy is only available with -r.\n");
+        use_fastdeploy = false;
+    }
+
+    if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
+        printf("Fast Deploy is only compatible with devices of API version %d or higher, "
+               "ignoring.\n",
+               kFastDeployMinApi);
+        use_fastdeploy = false;
+    }
+
+    std::vector<const char*> passthrough_argv;
+    for (int i = 0; i < argc; i++) {
+        if (std::find(processedArgIndicies.begin(), processedArgIndicies.end(), i) ==
+            processedArgIndicies.end()) {
+            passthrough_argv.push_back(argv[i]);
+        }
+    }
+
+    if (use_fastdeploy == true) {
+        fastdeploy_set_local_agent(use_localagent);
+        update_agent(agent_update_strategy);
+    }
+
+    switch (installMode) {
+        case INSTALL_PUSH:
+            return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
+                                      use_fastdeploy, use_localagent);
+        case INSTALL_STREAM:
+            return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
+                                        use_fastdeploy, use_localagent);
+        case INSTALL_DEFAULT:
+        default:
+            return 1;
+    }
+}
+
+int install_multiple_app(int argc, const char** argv) {
+    // Find all APK arguments starting at end.
+    // All other arguments passed through verbatim.
+    int first_apk = -1;
+    uint64_t total_size = 0;
+    for (int i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+
+        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+            struct stat sb;
+            if (stat(file, &sb) != -1) total_size += sb.st_size;
+            first_apk = i;
+        } else {
+            break;
+        }
+    }
+
+    if (first_apk == -1) return syntax_error("need APK file on command line");
+
+    std::string install_cmd;
+    if (_use_legacy_install()) {
+        install_cmd = "exec:pm";
+    } else {
+        install_cmd = "exec:cmd package";
+    }
+
+    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64,
+                                                  install_cmd.c_str(), total_size);
+    for (int i = 1; i < first_apk; i++) {
+        cmd += " " + escape_arg(argv[i]);
+    }
+
+    // Create install session
+    std::string error;
+    int fd = adb_connect(cmd, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    char buf[BUFSIZ];
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    int session_id = -1;
+    if (!strncmp("Success", buf, 7)) {
+        char* start = strrchr(buf, '[');
+        char* end = strrchr(buf, ']');
+        if (start && end) {
+            *end = '\0';
+            session_id = strtol(start + 1, nullptr, 10);
+        }
+    }
+    if (session_id < 0) {
+        fprintf(stderr, "adb: failed to create session\n");
+        fputs(buf, stderr);
+        return EXIT_FAILURE;
+    }
+
+    // Valid session, now stream the APKs
+    int success = 1;
+    for (int i = first_apk; i < argc; i++) {
+        const char* file = argv[i];
+        struct stat sb;
+        if (stat(file, &sb) == -1) {
+            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
+            success = 0;
+            goto finalize_session;
+        }
+
+        std::string cmd =
+                android::base::StringPrintf("%s install-write -S %" PRIu64 " %d %d_%s -",
+                                            install_cmd.c_str(), static_cast<uint64_t>(sb.st_size),
+                                            session_id, i, android::base::Basename(file).c_str());
+
+        int localFd = adb_open(file, O_RDONLY);
+        if (localFd < 0) {
+            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
+            success = 0;
+            goto finalize_session;
+        }
+
+        std::string error;
+        int remoteFd = adb_connect(cmd, &error);
+        if (remoteFd < 0) {
+            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
+            adb_close(localFd);
+            success = 0;
+            goto finalize_session;
+        }
+
+        copy_to_file(localFd, remoteFd);
+        read_status_line(remoteFd, buf, sizeof(buf));
+
+        adb_close(localFd);
+        adb_close(remoteFd);
+
+        if (strncmp("Success", buf, 7)) {
+            fprintf(stderr, "adb: failed to write %s\n", file);
+            fputs(buf, stderr);
+            success = 0;
+            goto finalize_session;
+        }
+    }
+
+finalize_session:
+    // Commit session if we streamed everything okay; otherwise abandon
+    std::string service = android::base::StringPrintf("%s install-%s %d", install_cmd.c_str(),
+                                                      success ? "commit" : "abandon", session_id);
+    fd = adb_connect(service, &error);
+    if (fd < 0) {
+        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
+        return EXIT_FAILURE;
+    }
+    read_status_line(fd, buf, sizeof(buf));
+    adb_close(fd);
+
+    if (!strncmp("Success", buf, 7)) {
+        fputs(buf, stdout);
+        return 0;
+    }
+    fprintf(stderr, "adb: failed to finalize session\n");
+    fputs(buf, stderr);
+    return EXIT_FAILURE;
+}
+
+int delete_device_file(const std::string& filename) {
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return send_shell_command(cmd);
+}
diff --git a/adb/client/adb_install.h b/adb/client/adb_install.h
new file mode 100644
index 0000000..e9410a9
--- /dev/null
+++ b/adb/client/adb_install.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ADB_INSTALL_H
+#define ADB_INSTALL_H
+
+#include "fastdeploy.h"
+
+int install_app(int argc, const char** argv);
+int install_multiple_app(int argc, const char** argv);
+int uninstall_app(int argc, const char** argv);
+
+int delete_device_file(const std::string& filename);
+int delete_host_file(const std::string& filename);
+
+#endif
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index 41ac663..6e143c1 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -52,23 +52,19 @@
 #include "adb.h"
 #include "adb_auth.h"
 #include "adb_client.h"
+#include "adb_install.h"
 #include "adb_io.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "bugreport.h"
 #include "client/file_sync_client.h"
 #include "commandline.h"
+#include "fastdeploy.h"
 #include "services.h"
 #include "shell_protocol.h"
 #include "sysdeps/chrono.h"
 #include "sysdeps/memory.h"
 
-static int install_app(int argc, const char** argv);
-static int install_multiple_app(int argc, const char** argv);
-static int uninstall_app(int argc, const char** argv);
-static int install_app_legacy(int argc, const char** argv);
-static int uninstall_app_legacy(int argc, const char** argv);
-
 extern int gListenAll;
 
 DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
@@ -134,7 +130,7 @@
         " pull [-a] REMOTE... LOCAL\n"
         "     copy files/dirs from device\n"
         "     -a: preserve file timestamp and mode\n"
-        " sync [all|data|odm|oem|product|system|vendor]\n"
+        " sync [all|data|odm|oem|product_services|product|system|vendor]\n"
         "     sync a local build from $ANDROID_PRODUCT_OUT to the device (default all)\n"
         "     -l: list but don't copy\n"
         "\n"
@@ -160,6 +156,17 @@
         "     -p: partial application install (install-multiple only)\n"
         "     -g: grant all runtime permissions\n"
         "     --instant: cause the app to be installed as an ephemeral install app\n"
+        "     --no-streaming: always push APK to device and invoke Package Manager as separate steps\n"
+        "     --streaming: force streaming APK directly into Package Manager\n"
+        "     --fastdeploy: use fast deploy (only valid with -r)\n"
+        "     --no-fastdeploy: prevent use of fast deploy (only valid with -r)\n"
+        "     --force-agent: force update of deployment agent when using fast deploy\n"
+        "     --date-check-agent: update deployment agent when local version is newer and using fast deploy\n"
+        "     --version-check-agent: update deployment agent when local version has different version code and using fast deploy\n"
+#ifndef _WIN32
+        "     --local-agent: locate agent files from local source build (instead of SDK location)\n"
+#endif
+        //TODO--installlog <filename>
         " uninstall [-k] PACKAGE\n"
         "     remove this app package from the device\n"
         "     '-k': keep the data and cache directories\n"
@@ -306,21 +313,6 @@
     return callback->Done(exit_code);
 }
 
-static void read_status_line(int fd, char* buf, size_t count)
-{
-    count--;
-    while (count > 0) {
-        int len = adb_read(fd, buf, count);
-        if (len <= 0) {
-            break;
-        }
-
-        buf += len;
-        count -= len;
-    }
-    *buf = '\0';
-}
-
 static void stdinout_raw_prologue(int inFd, int outFd, int& old_stdin_mode, int& old_stdout_mode) {
     if (inFd == STDIN_FILENO) {
         stdin_raw_init();
@@ -361,9 +353,8 @@
 #endif
 }
 
-static void copy_to_file(int inFd, int outFd) {
-    constexpr size_t BUFSIZE = 32 * 1024;
-    std::vector<char> buf(BUFSIZE);
+void copy_to_file(int inFd, int outFd) {
+    std::vector<char> buf(32 * 1024);
     int len;
     long total = 0;
     int old_stdin_mode = -1;
@@ -375,9 +366,9 @@
 
     while (true) {
         if (inFd == STDIN_FILENO) {
-            len = unix_read(inFd, buf.data(), BUFSIZE);
+            len = unix_read(inFd, buf.data(), buf.size());
         } else {
-            len = adb_read(inFd, buf.data(), BUFSIZE);
+            len = adb_read(inFd, buf.data(), buf.size());
         }
         if (len == 0) {
             D("copy_to_file() : read 0 bytes; exiting");
@@ -1315,16 +1306,6 @@
 #endif
 }
 
-static bool _use_legacy_install() {
-    FeatureSet features;
-    std::string error;
-    if (!adb_get_feature_set(&features, &error)) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return true;
-    }
-    return !CanUseFeature(features, kFeatureCmd);
-}
-
 int adb_commandline(int argc, const char** argv) {
     bool no_daemon = false;
     bool is_daemon = false;
@@ -1706,9 +1687,6 @@
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return syntax_error("install requires an argument");
-        if (_use_legacy_install()) {
-            return install_app_legacy(argc, argv);
-        }
         return install_app(argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
@@ -1717,9 +1695,6 @@
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return syntax_error("uninstall requires an argument");
-        if (_use_legacy_install()) {
-            return uninstall_app_legacy(argc, argv);
-        }
         return uninstall_app(argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
@@ -1737,7 +1712,8 @@
         }
 
         if (src.empty()) src = "all";
-        std::vector<std::string> partitions{"data", "odm", "oem", "product", "system", "vendor"};
+        std::vector<std::string> partitions{"data",   "odm",   "oem", "product", "product_services",
+                                            "system", "vendor"};
         bool found = false;
         for (const auto& partition : partitions) {
             if (src == "all" || src == partition) {
@@ -1844,270 +1820,3 @@
     syntax_error("unknown command %s", argv[0]);
     return 1;
 }
-
-static int uninstall_app(int argc, const char** argv) {
-    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
-    std::string cmd = "cmd package";
-    while (argc-- > 0) {
-        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
-        if (strcmp(*argv, "-k") == 0) {
-            printf(
-                "The -k option uninstalls the application while retaining the data/cache.\n"
-                "At the moment, there is no way to remove the remaining data.\n"
-                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-                "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
-            return EXIT_FAILURE;
-        }
-        cmd += " " + escape_arg(*argv++);
-    }
-
-    return send_shell_command(cmd);
-}
-
-static int install_app(int argc, const char** argv) {
-    // The last argument must be the APK file
-    const char* file = argv[argc - 1];
-    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
-        return syntax_error("filename doesn't end .apk: %s", file);
-    }
-
-    struct stat sb;
-    if (stat(file, &sb) == -1) {
-        fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-        return 1;
-    }
-
-    int localFd = adb_open(file, O_RDONLY);
-    if (localFd < 0) {
-        fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-        return 1;
-    }
-
-    std::string error;
-    std::string cmd = "exec:cmd package";
-
-    // don't copy the APK name, but, copy the rest of the arguments as-is
-    while (argc-- > 1) {
-        cmd += " " + escape_arg(std::string(*argv++));
-    }
-
-    // add size parameter [required for streaming installs]
-    // do last to override any user specified value
-    cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
-
-    int remoteFd = adb_connect(cmd, &error);
-    if (remoteFd < 0) {
-        fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-        adb_close(localFd);
-        return 1;
-    }
-
-    char buf[BUFSIZ];
-    copy_to_file(localFd, remoteFd);
-    read_status_line(remoteFd, buf, sizeof(buf));
-
-    adb_close(localFd);
-    adb_close(remoteFd);
-
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
-    }
-    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
-    return 1;
-}
-
-static int install_multiple_app(int argc, const char** argv) {
-    // Find all APK arguments starting at end.
-    // All other arguments passed through verbatim.
-    int first_apk = -1;
-    uint64_t total_size = 0;
-    for (int i = argc - 1; i >= 0; i--) {
-        const char* file = argv[i];
-
-        if (android::base::EndsWithIgnoreCase(file, ".apk") ||
-            android::base::EndsWithIgnoreCase(file, ".dm")) {
-            struct stat sb;
-            if (stat(file, &sb) != -1) total_size += sb.st_size;
-            first_apk = i;
-        } else {
-            break;
-        }
-    }
-
-    if (first_apk == -1) return syntax_error("need APK file on command line");
-
-    std::string install_cmd;
-    if (_use_legacy_install()) {
-        install_cmd = "exec:pm";
-    } else {
-        install_cmd = "exec:cmd package";
-    }
-
-    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
-    for (int i = 1; i < first_apk; i++) {
-        cmd += " " + escape_arg(argv[i]);
-    }
-
-    // Create install session
-    std::string error;
-    int fd = adb_connect(cmd, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
-        return EXIT_FAILURE;
-    }
-    char buf[BUFSIZ];
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
-
-    int session_id = -1;
-    if (!strncmp("Success", buf, 7)) {
-        char* start = strrchr(buf, '[');
-        char* end = strrchr(buf, ']');
-        if (start && end) {
-            *end = '\0';
-            session_id = strtol(start + 1, nullptr, 10);
-        }
-    }
-    if (session_id < 0) {
-        fprintf(stderr, "adb: failed to create session\n");
-        fputs(buf, stderr);
-        return EXIT_FAILURE;
-    }
-
-    // Valid session, now stream the APKs
-    int success = 1;
-    for (int i = first_apk; i < argc; i++) {
-        const char* file = argv[i];
-        struct stat sb;
-        if (stat(file, &sb) == -1) {
-            fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
-            success = 0;
-            goto finalize_session;
-        }
-
-        std::string cmd = android::base::StringPrintf(
-            "%s install-write -S %" PRIu64 " %d %s -", install_cmd.c_str(),
-            static_cast<uint64_t>(sb.st_size), session_id, android::base::Basename(file).c_str());
-
-        int localFd = adb_open(file, O_RDONLY);
-        if (localFd < 0) {
-            fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
-            success = 0;
-            goto finalize_session;
-        }
-
-        std::string error;
-        int remoteFd = adb_connect(cmd, &error);
-        if (remoteFd < 0) {
-            fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
-            adb_close(localFd);
-            success = 0;
-            goto finalize_session;
-        }
-
-        copy_to_file(localFd, remoteFd);
-        read_status_line(remoteFd, buf, sizeof(buf));
-
-        adb_close(localFd);
-        adb_close(remoteFd);
-
-        if (strncmp("Success", buf, 7)) {
-            fprintf(stderr, "adb: failed to write %s\n", file);
-            fputs(buf, stderr);
-            success = 0;
-            goto finalize_session;
-        }
-    }
-
-finalize_session:
-    // Commit session if we streamed everything okay; otherwise abandon
-    std::string service =
-            android::base::StringPrintf("%s install-%s %d",
-                                        install_cmd.c_str(), success ? "commit" : "abandon", session_id);
-    fd = adb_connect(service, &error);
-    if (fd < 0) {
-        fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
-        return EXIT_FAILURE;
-    }
-    read_status_line(fd, buf, sizeof(buf));
-    adb_close(fd);
-
-    if (!strncmp("Success", buf, 7)) {
-        fputs(buf, stdout);
-        return 0;
-    }
-    fprintf(stderr, "adb: failed to finalize session\n");
-    fputs(buf, stderr);
-    return EXIT_FAILURE;
-}
-
-static int pm_command(int argc, const char** argv) {
-    std::string cmd = "pm";
-
-    while (argc-- > 0) {
-        cmd += " " + escape_arg(*argv++);
-    }
-
-    return send_shell_command(cmd);
-}
-
-static int uninstall_app_legacy(int argc, const char** argv) {
-    /* if the user choose the -k option, we refuse to do it until devices are
-       out with the option to uninstall the remaining data somehow (adb/ui) */
-    int i;
-    for (i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-k")) {
-            printf(
-                "The -k option uninstalls the application while retaining the data/cache.\n"
-                "At the moment, there is no way to remove the remaining data.\n"
-                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-                "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
-            return EXIT_FAILURE;
-        }
-    }
-
-    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
-    return pm_command(argc, argv);
-}
-
-static int delete_file(const std::string& filename) {
-    std::string cmd = "rm -f " + escape_arg(filename);
-    return send_shell_command(cmd);
-}
-
-static int install_app_legacy(int argc, const char** argv) {
-    static const char *const DATA_DEST = "/data/local/tmp/%s";
-    static const char *const SD_DEST = "/sdcard/tmp/%s";
-    const char* where = DATA_DEST;
-
-    for (int i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-s")) {
-            where = SD_DEST;
-        }
-    }
-
-    // Find last APK argument.
-    // All other arguments passed through verbatim.
-    int last_apk = -1;
-    for (int i = argc - 1; i >= 0; i--) {
-        if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
-            last_apk = i;
-            break;
-        }
-    }
-
-    if (last_apk == -1) return syntax_error("need APK file on command line");
-
-    int result = -1;
-    std::vector<const char*> apk_file = {argv[last_apk]};
-    std::string apk_dest = android::base::StringPrintf(
-        where, android::base::Basename(argv[last_apk]).c_str());
-    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
-    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
-    result = pm_command(argc, argv);
-
-cleanup_apk:
-    delete_file(apk_dest);
-    return result;
-}
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index 3aa03a7..6cfd4f7 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -96,6 +96,8 @@
 
 int adb_commandline(int argc, const char** argv);
 
+void copy_to_file(int inFd, int outFd);
+
 // Connects to the device "shell" service with |command| and prints the
 // resulting output.
 // if |callback| is non-null, stdout/stderr output will be handled by it.
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
new file mode 100644
index 0000000..d3f35c8
--- /dev/null
+++ b/adb/client/fastdeploy.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libgen.h>
+#include <algorithm>
+#include <array>
+
+#include "android-base/file.h"
+#include "android-base/strings.h"
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "fastdeploy.h"
+#include "fastdeploycallbacks.h"
+#include "utils/String16.h"
+
+static constexpr long kRequiredAgentVersion = 0x00000001;
+
+static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+
+static bool g_use_localagent = false;
+
+long get_agent_version() {
+    std::vector<char> versionOutputBuffer;
+    std::vector<char> versionErrorBuffer;
+
+    int statusCode = capture_shell_command("/data/local/tmp/deployagent version",
+                                           &versionOutputBuffer, &versionErrorBuffer);
+    long version = -1;
+
+    if (statusCode == 0 && versionOutputBuffer.size() > 0) {
+        version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
+    }
+
+    return version;
+}
+
+int get_device_api_level() {
+    std::vector<char> sdkVersionOutputBuffer;
+    std::vector<char> sdkVersionErrorBuffer;
+    int api_level = -1;
+
+    int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
+                                           &sdkVersionErrorBuffer);
+    if (statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
+        api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
+    }
+
+    return api_level;
+}
+
+void fastdeploy_set_local_agent(bool use_localagent) {
+    g_use_localagent = use_localagent;
+}
+
+// local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
+static std::string get_agent_component_host_path(const char* local_path, const char* sdk_path) {
+    std::string adb_dir = android::base::GetExecutableDirectory();
+    if (adb_dir.empty()) {
+        fatal("Could not determine location of adb!");
+    }
+
+    if (g_use_localagent) {
+        const char* product_out = getenv("ANDROID_PRODUCT_OUT");
+        if (product_out == nullptr) {
+            fatal("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) {
+            fatal("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
+        }
+    } else {
+        fatal("Error pushing agent files to device");
+    }
+
+    return true;
+}
+
+void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy) {
+    long agent_version = get_agent_version();
+    switch (agentUpdateStrategy) {
+        case FastDeploy_AgentUpdateAlways:
+            deploy_agent(false);
+            break;
+        case FastDeploy_AgentUpdateNewerTimeStamp:
+            deploy_agent(true);
+            break;
+        case FastDeploy_AgentUpdateDifferentVersion:
+            if (agent_version != kRequiredAgentVersion) {
+                if (agent_version < 0) {
+                    printf("Could not detect agent on device, deploying\n");
+                } else {
+                    printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
+                           agent_version, kRequiredAgentVersion);
+                }
+                deploy_agent(false);
+            }
+            break;
+    }
+
+    agent_version = get_agent_version();
+    if (agent_version != kRequiredAgentVersion) {
+        fatal("After update agent version remains incorrect! Expected %ld but version is %ld",
+              kRequiredAgentVersion, agent_version);
+    }
+}
+
+static std::string get_aapt2_path() {
+    if (g_use_localagent) {
+        // This should never happen on a Windows machine
+        const char* host_out = getenv("ANDROID_HOST_OUT");
+        if (host_out == nullptr) {
+            fatal("Could not locate aapt2 because $ANDROID_HOST_OUT is not defined");
+        }
+        return android::base::StringPrintf("%s/bin/aapt2", host_out);
+    }
+
+    std::string adb_dir = android::base::GetExecutableDirectory();
+    if (adb_dir.empty()) {
+        fatal("Could not locate aapt2");
+    }
+    return adb_dir + "/aapt2";
+}
+
+static int system_capture(const char* cmd, std::string& output) {
+    FILE* pipe = popen(cmd, "re");
+    int fd = -1;
+
+    if (pipe != nullptr) {
+        fd = fileno(pipe);
+    }
+
+    if (fd == -1) {
+        fatal_errno("Could not create pipe for process '%s'", cmd);
+    }
+
+    if (!android::base::ReadFdToString(fd, &output)) {
+        fatal_errno("Error reading from process '%s'", cmd);
+    }
+
+    return pclose(pipe);
+}
+
+// output is required to point to a valid output string (non-null)
+static std::string get_packagename_from_apk(const char* apkPath) {
+    const char* kAapt2DumpNameCommandPattern = R"(%s dump packagename "%s")";
+    std::string aapt2_path_string = get_aapt2_path();
+    std::string getPackagenameCommand = android::base::StringPrintf(
+            kAapt2DumpNameCommandPattern, aapt2_path_string.c_str(), apkPath);
+
+    std::string package_name;
+    int exit_code = system_capture(getPackagenameCommand.c_str(), package_name);
+    if (exit_code != 0) {
+        fatal("Error executing '%s' exitcode: %d", getPackagenameCommand.c_str(), exit_code);
+    }
+
+    // strip any line end characters from the output
+    return android::base::Trim(package_name);
+}
+
+int extract_metadata(const char* apkPath, FILE* outputFp) {
+    std::string packageName = get_packagename_from_apk(apkPath);
+    const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent extract %s";
+    std::string extractCommand =
+            android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
+
+    std::vector<char> extractErrorBuffer;
+    int statusCode;
+    DeployAgentFileCallback cb(outputFp, &extractErrorBuffer, &statusCode);
+    int ret = send_shell_command(extractCommand, false, &cb);
+
+    if (ret == 0) {
+        return cb.getBytesWritten();
+    }
+
+    return ret;
+}
+
+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) {
+            fatal("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()) {
+        fatal("Could not locate deploypatchgenerator.jar");
+    }
+    return android::base::StringPrintf(R"(java -jar "%s/deploypatchgenerator.jar")",
+                                       adb_dir.c_str());
+}
+
+int 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);
+    return system(generatePatchCommand.c_str());
+}
+
+std::string get_patch_path(const char* apkPath) {
+    std::string packageName = get_packagename_from_apk(apkPath);
+    std::string patchDevicePath =
+            android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
+    return patchDevicePath;
+}
+
+int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
+    const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -o %s";
+    std::string packageName = get_packagename_from_apk(apkPath);
+    std::string patchDevicePath = get_patch_path(apkPath);
+
+    std::vector<const char*> srcs = {patchPath};
+    bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
+
+    if (!push_ok) {
+        return -1;
+    }
+
+    std::string applyPatchCommand =
+            android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
+                                        patchDevicePath.c_str(), outputPath);
+
+    return send_shell_command(applyPatchCommand);
+}
+
+int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
+    const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
+    std::string packageName = get_packagename_from_apk(apkPath);
+    std::vector<const char*> srcs;
+    std::string patchDevicePath =
+            android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
+    srcs.push_back(patchPath);
+    bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
+
+    if (!push_ok) {
+        return -1;
+    }
+
+    std::vector<unsigned char> applyOutputBuffer;
+    std::vector<unsigned char> applyErrorBuffer;
+    std::string argsString;
+
+    for (int i = 0; i < argc; i++) {
+        argsString.append(argv[i]);
+        argsString.append(" ");
+    }
+
+    std::string applyPatchCommand =
+            android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
+                                        patchDevicePath.c_str(), argsString.c_str());
+    return send_shell_command(applyPatchCommand);
+}
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
new file mode 100644
index 0000000..e5e7663
--- /dev/null
+++ b/adb/client/fastdeploy.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "adb.h"
+
+enum FastDeploy_AgentUpdateStrategy {
+    FastDeploy_AgentUpdateAlways,
+    FastDeploy_AgentUpdateNewerTimeStamp,
+    FastDeploy_AgentUpdateDifferentVersion
+};
+
+void fastdeploy_set_local_agent(bool use_localagent);
+int get_device_api_level();
+void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy);
+int extract_metadata(const char* apkPath, FILE* outputFp);
+int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
+int apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
+int install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
+std::string get_patch_path(const char* apkPath);
diff --git a/adb/client/fastdeploycallbacks.cpp b/adb/client/fastdeploycallbacks.cpp
new file mode 100644
index 0000000..6c9a21f
--- /dev/null
+++ b/adb/client/fastdeploycallbacks.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG ADB
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "client/file_sync_client.h"
+#include "commandline.h"
+#include "sysdeps.h"
+
+#include "fastdeploycallbacks.h"
+
+static void appendBuffer(std::vector<char>* buffer, const char* input, int length) {
+    if (buffer != NULL) {
+        buffer->insert(buffer->end(), input, input + length);
+    }
+}
+
+class DeployAgentBufferCallback : public StandardStreamsCallbackInterface {
+  public:
+    DeployAgentBufferCallback(std::vector<char>* outBuffer, std::vector<char>* errBuffer,
+                              int* statusCode);
+
+    virtual void OnStdout(const char* buffer, int length);
+    virtual void OnStderr(const char* buffer, int length);
+    virtual int Done(int status);
+
+  private:
+    std::vector<char>* mpOutBuffer;
+    std::vector<char>* mpErrBuffer;
+    int* mpStatusCode;
+};
+
+int capture_shell_command(const char* command, std::vector<char>* outBuffer,
+                          std::vector<char>* errBuffer) {
+    int statusCode;
+    DeployAgentBufferCallback cb(outBuffer, errBuffer, &statusCode);
+    int ret = send_shell_command(command, false, &cb);
+
+    if (ret == 0) {
+        return statusCode;
+    } else {
+        return ret;
+    }
+}
+
+DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer,
+                                                 int* statusCode) {
+    mpOutFile = outputFile;
+    mpErrBuffer = errBuffer;
+    mpStatusCode = statusCode;
+    mBytesWritten = 0;
+}
+
+void DeployAgentFileCallback::OnStdout(const char* buffer, int length) {
+    if (mpOutFile != NULL) {
+        int bytes_written = fwrite(buffer, 1, length, mpOutFile);
+        if (bytes_written != length) {
+            printf("Write error %d\n", bytes_written);
+        }
+        mBytesWritten += bytes_written;
+    }
+}
+
+void DeployAgentFileCallback::OnStderr(const char* buffer, int length) {
+    appendBuffer(mpErrBuffer, buffer, length);
+}
+
+int DeployAgentFileCallback::Done(int status) {
+    if (mpStatusCode != NULL) {
+        *mpStatusCode = status;
+    }
+    return 0;
+}
+
+int DeployAgentFileCallback::getBytesWritten() {
+    return mBytesWritten;
+}
+
+DeployAgentBufferCallback::DeployAgentBufferCallback(std::vector<char>* outBuffer,
+                                                     std::vector<char>* errBuffer,
+                                                     int* statusCode) {
+    mpOutBuffer = outBuffer;
+    mpErrBuffer = errBuffer;
+    mpStatusCode = statusCode;
+}
+
+void DeployAgentBufferCallback::OnStdout(const char* buffer, int length) {
+    appendBuffer(mpOutBuffer, buffer, length);
+}
+
+void DeployAgentBufferCallback::OnStderr(const char* buffer, int length) {
+    appendBuffer(mpErrBuffer, buffer, length);
+}
+
+int DeployAgentBufferCallback::Done(int status) {
+    if (mpStatusCode != NULL) {
+        *mpStatusCode = status;
+    }
+    return 0;
+}
diff --git a/adb/client/fastdeploycallbacks.h b/adb/client/fastdeploycallbacks.h
new file mode 100644
index 0000000..b428b50
--- /dev/null
+++ b/adb/client/fastdeploycallbacks.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+#include "commandline.h"
+
+class DeployAgentFileCallback : public StandardStreamsCallbackInterface {
+  public:
+    DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer, int* statusCode);
+
+    virtual void OnStdout(const char* buffer, int length);
+    virtual void OnStderr(const char* buffer, int length);
+    virtual int Done(int status);
+
+    int getBytesWritten();
+
+  private:
+    FILE* mpOutFile;
+    std::vector<char>* mpErrBuffer;
+    int mBytesWritten;
+    int* mpStatusCode;
+};
+
+int capture_shell_command(const char* command, std::vector<char>* outBuffer,
+                          std::vector<char>* errBuffer);
diff --git a/adb/client/line_printer.cpp b/adb/client/line_printer.cpp
index 9758526..4dc2d28 100644
--- a/adb/client/line_printer.cpp
+++ b/adb/client/line_printer.cpp
@@ -67,7 +67,11 @@
 
 void LinePrinter::Print(string to_print, LineType type) {
   if (!smart_terminal_) {
-    Out(to_print + "\n");
+    if (type == LineType::INFO) {
+        info_line_ = to_print + "\n";
+    } else {
+        Out(to_print + "\n");
+    }
     return;
   }
 
@@ -123,6 +127,11 @@
 }
 
 void LinePrinter::KeepInfoLine() {
-  if (!have_blank_line_) Out("\n");
-  have_blank_line_ = true;
+  if (smart_terminal_) {
+      if (!have_blank_line_) Out("\n");
+      have_blank_line_ = true;
+  } else {
+      Out(info_line_);
+      info_line_.clear();
+  }
 }
diff --git a/adb/client/line_printer.h b/adb/client/line_printer.h
index 42345e2..4c4c7c6 100644
--- a/adb/client/line_printer.h
+++ b/adb/client/line_printer.h
@@ -42,6 +42,9 @@
   /// Whether the caret is at the beginning of a blank line.
   bool have_blank_line_;
 
+  /// The last printed info line when printing to a dumb terminal.
+  std::string info_line_;
+
 #ifdef _WIN32
   void* console_;
 #endif
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 095ad98..a7e454d 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -56,15 +56,6 @@
     LOG(INFO) << adb_version();
 }
 
-#if defined(_WIN32)
-static BOOL WINAPI ctrlc_handler(DWORD type) {
-    // TODO: Consider trying to kill a starting up adb server (if we're in
-    // launch_server) by calling GenerateConsoleCtrlEvent().
-    exit(STATUS_CONTROL_C_EXIT);
-    return TRUE;
-}
-#endif
-
 void adb_server_cleanup() {
     // Upon exit, we want to clean up in the following order:
     //   1. close_smartsockets, so that we don't get any new clients
@@ -97,12 +88,16 @@
         }
     }
 
-    SetConsoleCtrlHandler(ctrlc_handler, TRUE);
-#else
+    // TODO: On Ctrl-C, consider trying to kill a starting up adb server (if we're in
+    // launch_server) by calling GenerateConsoleCtrlEvent().
+
+    // On Windows, SIGBREAK is when Ctrl-Break is pressed or the console window is closed. It should
+    // act like Ctrl-C.
+    signal(SIGBREAK, [](int) { raise(SIGINT); });
+#endif
     signal(SIGINT, [](int) {
         fdevent_run_on_main_thread([]() { exit(0); });
     });
-#endif
 
     char* leak = getenv("ADB_LEAK");
     if (leak && strcmp(leak, "1") == 0) {
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 869e858..f1bf559 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -30,6 +30,7 @@
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/time.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
 
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index e928377..00aeb9b 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -357,7 +357,8 @@
 
     if (handle->zero_mask && (len & handle->zero_mask) == 0) {
         // Send a zero length packet
-        if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, 0, &written, time_out)) {
+        unsigned long dummy;
+        if (!AdbWriteEndpointSync(handle->adb_write_pipe, (void*)data, 0, &dummy, time_out)) {
             D("AdbWriteEndpointSync of zero length packet failed: %s",
               android::base::SystemErrorCodeToString(GetLastError()).c_str());
             err = EIO;
diff --git a/adb/daemon/include/adbd/usb.h b/adb/daemon/include/adbd/usb.h
index 7905d9d..78e4cd5 100644
--- a/adb/daemon/include/adbd/usb.h
+++ b/adb/daemon/include/adbd/usb.h
@@ -21,6 +21,7 @@
 #include <mutex>
 #include <vector>
 
+#include <android-base/unique_fd.h>
 #include <asyncio/AsyncIO.h>
 
 struct aio_block {
@@ -47,11 +48,11 @@
     void (*close)(usb_handle* h);
 
     // FunctionFS
-    int control = -1;
-    int bulk_out = -1; /* "out" from the host's perspective => source for adbd */
-    int bulk_in = -1;  /* "in" from the host's perspective => sink for adbd */
+    android::base::unique_fd control;
+    android::base::unique_fd bulk_out;  // "out" from the host's perspective => source for adbd
+    android::base::unique_fd bulk_in;   // "in" from the host's perspective => sink for adbd
 
-    // Access to these blocks is very not thread safe. Have one block for both the
+    // Access to these blocks is very not thread safe. Have one block for each of the
     // read and write threads.
     struct aio_block read_aiob;
     struct aio_block write_aiob;
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index f7017dd..380dfa6 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -38,7 +38,6 @@
 #include <android-base/properties.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
 #include <fs_mgr_overlayfs.h>
 
@@ -96,27 +95,6 @@
     return result;
 }
 
-static bool fs_has_shared_blocks(const std::string& mount_point, const std::string& device) {
-    std::string path = mount_point + "/lost+found";
-    struct statfs fs;
-    if (statfs(path.c_str(), &fs) == -1 || fs.f_type != EXT4_SUPER_MAGIC) {
-        return false;
-    }
-    unique_fd fd(unix_open(device.c_str(), O_RDONLY));
-    if (fd < 0) {
-        return false;
-    }
-    struct ext4_super_block sb;
-    if (lseek64(fd, 1024, SEEK_SET) < 0 || unix_read(fd, &sb, sizeof(sb)) < 0) {
-        return false;
-    }
-    struct fs_info info;
-    if (ext4_parse_sb(&sb, &info) < 0) {
-        return false;
-    }
-    return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
-}
-
 static bool can_unshare_blocks(int fd, const char* dev) {
     const char* E2FSCK_BIN = "/system/bin/e2fsck";
     if (access(E2FSCK_BIN, X_OK)) {
@@ -168,6 +146,10 @@
         return true;
     }
     bool is_root = strcmp(dir, "/") == 0;
+    if (is_root && !find_mount("/system", false).empty()) {
+        dir = "/system";
+        is_root = false;
+    }
     std::string dev = find_mount(dir, is_root);
     // Even if the device for the root is not found, we still try to remount it
     // as rw. This typically only happens when running Android in a container:
@@ -231,26 +213,28 @@
     bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty());
     bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty());
 
-    std::vector<std::string> partitions = {"/odm", "/oem", "/product", "/vendor"};
-    if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
-        partitions.push_back("/");
-    } else {
-        partitions.push_back("/system");
-    }
+    std::vector<std::string> partitions{"/",        "/odm",   "/oem", "/product_services",
+                                        "/product", "/vendor"};
 
     bool verity_enabled = (system_verified || vendor_verified);
 
     // If we can use overlayfs, lets get it in place first
     // before we struggle with determining deduplication operations.
-    if (!verity_enabled && fs_mgr_overlayfs_setup() && fs_mgr_overlayfs_mount_all()) {
-        WriteFdExactly(fd.get(), "overlayfs mounted\n");
+    if (!verity_enabled && fs_mgr_overlayfs_setup()) {
+        std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                                   fs_mgr_free_fstab);
+        if (fs_mgr_overlayfs_mount_all(fstab.get())) {
+            WriteFdExactly(fd.get(), "overlayfs mounted\n");
+        }
     }
 
     // Find partitions that are deduplicated, and can be un-deduplicated.
     std::set<std::string> dedup;
-    for (const auto& partition : partitions) {
+    for (const auto& part : partitions) {
+        auto partition = part;
+        if ((part == "/") && !find_mount("/system", false).empty()) partition = "/system";
         std::string dev = find_mount(partition.c_str(), partition == "/");
-        if (dev.empty() || !fs_has_shared_blocks(partition, dev)) {
+        if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) {
             continue;
         }
         if (can_unshare_blocks(fd.get(), dev.c_str())) {
diff --git a/adb/daemon/services.cpp b/adb/daemon/services.cpp
index 8417690..2bac486 100644
--- a/adb/daemon/services.cpp
+++ b/adb/daemon/services.cpp
@@ -103,7 +103,9 @@
     if (reboot_arg.empty()) reboot_arg = "adb";
     std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg.c_str());
 
-    if (reboot_arg == "fastboot" && access("/dev/socket/recovery", F_OK) == 0) {
+    if (reboot_arg == "fastboot" &&
+        android::base::GetBoolProperty("ro.boot.logical_partitions", false) &&
+        access("/dev/socket/recovery", F_OK) == 0) {
         LOG(INFO) << "Recovery specific reboot fastboot";
         /*
          * The socket is created to allow switching between recovery and
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index c79d6fc..9d495b0 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -58,7 +58,7 @@
 #define cpu_to_le16(x) htole16(x)
 #define cpu_to_le32(x) htole32(x)
 
-static int dummy_fd = -1;
+static unique_fd& dummy_fd = *new unique_fd();
 
 struct func_desc {
     struct usb_interface_descriptor intf;
@@ -273,13 +273,13 @@
 
     if (h->control < 0) { // might have already done this before
         LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
-        h->control = adb_open(USB_FFS_ADB_EP0, O_WRONLY);
+        h->control.reset(adb_open(USB_FFS_ADB_EP0, O_WRONLY));
         if (h->control < 0) {
             PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
             goto err;
         }
 
-        ret = adb_write(h->control, &v2_descriptor, sizeof(v2_descriptor));
+        ret = adb_write(h->control.get(), &v2_descriptor, sizeof(v2_descriptor));
         if (ret < 0) {
             v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
             v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
@@ -288,14 +288,14 @@
             v1_descriptor.fs_descs = fs_descriptors;
             v1_descriptor.hs_descs = hs_descriptors;
             D("[ %s: Switching to V1_descriptor format errno=%d ]", USB_FFS_ADB_EP0, errno);
-            ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor));
+            ret = adb_write(h->control.get(), &v1_descriptor, sizeof(v1_descriptor));
             if (ret < 0) {
                 D("[ %s: write descriptors failed: errno=%d ]", USB_FFS_ADB_EP0, errno);
                 goto err;
             }
         }
 
-        ret = adb_write(h->control, &strings, sizeof(strings));
+        ret = adb_write(h->control.get(), &strings, sizeof(strings));
         if (ret < 0) {
             D("[ %s: writing strings failed: errno=%d]", USB_FFS_ADB_EP0, errno);
             goto err;
@@ -304,13 +304,13 @@
         android::base::SetProperty("sys.usb.ffs.ready", "1");
     }
 
-    h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDONLY);
+    h->bulk_out.reset(adb_open(USB_FFS_ADB_OUT, O_RDONLY));
     if (h->bulk_out < 0) {
         PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
         goto err;
     }
 
-    h->bulk_in = adb_open(USB_FFS_ADB_IN, O_WRONLY);
+    h->bulk_in.reset(adb_open(USB_FFS_ADB_IN, O_WRONLY));
     if (h->bulk_in < 0) {
         PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
         goto err;
@@ -322,18 +322,9 @@
     return true;
 
 err:
-    if (h->bulk_in > 0) {
-        adb_close(h->bulk_in);
-        h->bulk_in = -1;
-    }
-    if (h->bulk_out > 0) {
-        adb_close(h->bulk_out);
-        h->bulk_out = -1;
-    }
-    if (h->control > 0) {
-        adb_close(h->control);
-        h->control = -1;
-    }
+    h->bulk_in.reset();
+    h->bulk_out.reset();
+    h->control.reset();
     return false;
 }
 
@@ -366,7 +357,7 @@
 }
 
 static int usb_ffs_write(usb_handle* h, const void* data, int len) {
-    D("about to write (fd=%d, len=%d)", h->bulk_in, len);
+    D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len);
 
     const char* buf = static_cast<const char*>(data);
     int orig_len = len;
@@ -374,19 +365,19 @@
         int write_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_write(h->bulk_in, buf, write_len);
         if (n < 0) {
-            D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
+            D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno));
             return -1;
         }
         buf += n;
         len -= n;
     }
 
-    D("[ done fd=%d ]", h->bulk_in);
+    D("[ done fd=%d ]", h->bulk_in.get());
     return orig_len;
 }
 
 static int usb_ffs_read(usb_handle* h, void* data, int len) {
-    D("about to read (fd=%d, len=%d)", h->bulk_out, len);
+    D("about to read (fd=%d, len=%d)", h->bulk_out.get(), len);
 
     char* buf = static_cast<char*>(data);
     int orig_len = len;
@@ -394,14 +385,14 @@
         int read_len = std::min(USB_FFS_BULK_SIZE, len);
         int n = adb_read(h->bulk_out, buf, read_len);
         if (n < 0) {
-            D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
+            D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno));
             return -1;
         }
         buf += n;
         len -= n;
     }
 
-    D("[ done fd=%d ]", h->bulk_out);
+    D("[ done fd=%d ]", h->bulk_out.get());
     return orig_len;
 }
 
@@ -475,14 +466,14 @@
 static void usb_ffs_kick(usb_handle* h) {
     int err;
 
-    err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
+    err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT);
     if (err < 0) {
-        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
+        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno);
     }
 
-    err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
+    err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT);
     if (err < 0) {
-        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
+        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno);
     }
 
     // don't close ep0 here, since we may not need to reinitialize it with
@@ -490,16 +481,16 @@
     // init_functionfs, only then would we close and open ep0 again.
     // Ditto the comment in usb_adb_kick.
     h->kicked = true;
-    TEMP_FAILURE_RETRY(dup2(dummy_fd, h->bulk_out));
-    TEMP_FAILURE_RETRY(dup2(dummy_fd, h->bulk_in));
+    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get()));
+    TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get()));
 }
 
 static void usb_ffs_close(usb_handle* h) {
     LOG(INFO) << "closing functionfs transport";
 
     h->kicked = false;
-    adb_close(h->bulk_out);
-    adb_close(h->bulk_in);
+    h->bulk_out.reset();
+    h->bulk_in.reset();
 
     // Notify usb_adb_open_thread to open a new connection.
     h->lock.lock();
@@ -530,8 +521,8 @@
 
 void usb_init() {
     D("[ usb_init - using FunctionFS ]");
-    dummy_fd = adb_open("/dev/null", O_WRONLY);
-    CHECK_NE(dummy_fd, -1);
+    dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC));
+    CHECK_NE(-1, dummy_fd.get());
 
     std::thread(usb_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE)).detach();
 }
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
new file mode 100644
index 0000000..400b12f
--- /dev/null
+++ b/adb/fastdeploy/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+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",
+    }
+}
+
+java_binary_host {
+    name: "deploypatchgenerator",
+    srcs: ["deploypatchgenerator/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
+    static_libs: ["apkzlib"],
+    manifest: "deploypatchgenerator/manifest.txt",
+    proto: {
+        type: "full",
+    }
+}
diff --git a/adb/fastdeploy/deployagent/deployagent.sh b/adb/fastdeploy/deployagent/deployagent.sh
new file mode 100755
index 0000000..4f17eb7
--- /dev/null
+++ b/adb/fastdeploy/deployagent/deployagent.sh
@@ -0,0 +1,7 @@
+# Script to start "deployagent" on the device, which has a very rudimentary
+# shell.
+#
+base=/data/local/tmp
+export CLASSPATH=$base/deployagent.jar
+exec app_process $base com.android.fastdeploy.DeployAgent "$@"
+
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
new file mode 100644
index 0000000..cd6f168
--- /dev/null
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fastdeploy;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Set;
+
+import com.android.fastdeploy.APKMetaData;
+import com.android.fastdeploy.PatchUtils;
+
+public final class DeployAgent {
+    private static final int BUFFER_SIZE = 128 * 1024;
+    private static final int AGENT_VERSION = 0x00000001;
+
+    public static void main(String[] args) {
+        int exitCode = 0;
+        try {
+            if (args.length < 1) {
+                showUsage(0);
+            }
+
+            String commandString = args[0];
+
+            if (commandString.equals("extract")) {
+                if (args.length != 2) {
+                    showUsage(1);
+                }
+
+                String packageName = args[1];
+                extractMetaData(packageName);
+            } else if (commandString.equals("apply")) {
+                if (args.length < 4) {
+                    showUsage(1);
+                }
+
+                String packageName = args[1];
+                String patchPath = args[2];
+                String outputParam = args[3];
+
+                InputStream deltaInputStream = null;
+                if (patchPath.compareTo("-") == 0) {
+                    deltaInputStream = System.in;
+                } else {
+                    deltaInputStream = new FileInputStream(patchPath);
+                }
+
+                if (outputParam.equals("-o")) {
+                    OutputStream outputStream = null;
+                    if (args.length > 4) {
+                        String outputPath = args[4];
+                        if (!outputPath.equals("-")) {
+                            outputStream = new FileOutputStream(outputPath);
+                        }
+                    }
+                    if (outputStream == null) {
+                        outputStream = System.out;
+                    }
+                    File deviceFile = getFileFromPackageName(packageName);
+                    writePatchToStream(
+                            new RandomAccessFile(deviceFile, "r"), deltaInputStream, outputStream);
+                } else if (outputParam.equals("-pm")) {
+                    String[] sessionArgs = null;
+                    if (args.length > 4) {
+                        int numSessionArgs = args.length-4;
+                        sessionArgs = new String[numSessionArgs];
+                        for (int i=0 ; i<numSessionArgs ; i++) {
+                            sessionArgs[i] = args[i+4];
+                        }
+                    }
+                    exitCode = applyPatch(packageName, deltaInputStream, sessionArgs);
+                }
+            } else if (commandString.equals("version")) {
+                System.out.printf("0x%08X\n", AGENT_VERSION);
+            } else {
+                showUsage(1);
+            }
+        } catch (Exception e) {
+            System.err.println("Error: " + e);
+            e.printStackTrace();
+            System.exit(2);
+        }
+        System.exit(exitCode);
+    }
+
+    private static void showUsage(int exitCode) {
+        System.err.println(
+            "usage: deployagent <command> [<args>]\n\n" +
+            "commands:\n" +
+            "version                             get the version\n" +
+            "extract PKGNAME                     extract an installed package's metadata\n" +
+            "apply PKGNAME PATCHFILE [-o|-pm]    apply a patch from PATCHFILE (- for stdin) to an installed package\n" +
+            " -o <FILE> directs output to FILE, default or - for stdout\n" +
+            " -pm <ARGS> directs output to package manager, passes <ARGS> to 'pm install-create'\n"
+            );
+
+        System.exit(exitCode);
+    }
+
+    private static Process executeCommand(String command) throws IOException {
+        try {
+            Process p;
+            p = Runtime.getRuntime().exec(command);
+            p.waitFor();
+            return p;
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    private static File getFileFromPackageName(String packageName) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append("pm list packages -f " + packageName);
+
+        Process p = executeCommand(commandBuilder.toString());
+        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+
+        String packagePrefix = "package:";
+        String line = "";
+        while ((line = reader.readLine()) != null) {
+            int packageIndex = line.indexOf(packagePrefix);
+            int equalsIndex = line.indexOf("=" + packageName);
+            return new File(line.substring(packageIndex + packagePrefix.length(), equalsIndex));
+        }
+
+        return null;
+    }
+
+    private static void extractMetaData(String packageName) throws IOException {
+        File apkFile = getFileFromPackageName(packageName);
+        APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
+        apkMetaData.writeDelimitedTo(System.out);
+    }
+
+    private static int createInstallSession(String[] args) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append("pm install-create ");
+        for (int i=0 ; args != null && i<args.length ; i++) {
+            commandBuilder.append(args[i] + " ");
+        }
+
+        Process p = executeCommand(commandBuilder.toString());
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+        String line = "";
+        String successLineStart = "Success: created install session [";
+        String successLineEnd = "]";
+        while ((line = reader.readLine()) != null) {
+            if (line.startsWith(successLineStart) && line.endsWith(successLineEnd)) {
+                return Integer.parseInt(line.substring(successLineStart.length(), line.lastIndexOf(successLineEnd)));
+            }
+        }
+
+        return -1;
+    }
+
+    private static int commitInstallSession(int sessionId) throws IOException {
+        StringBuilder commandBuilder = new StringBuilder();
+        commandBuilder.append(String.format("pm install-commit %d -- - ", sessionId));
+        Process p = executeCommand(commandBuilder.toString());
+        return p.exitValue();
+    }
+
+    private static int applyPatch(String packageName, InputStream deltaStream, String[] sessionArgs)
+            throws IOException, PatchFormatException {
+        File deviceFile = getFileFromPackageName(packageName);
+        int sessionId = createInstallSession(sessionArgs);
+        if (sessionId < 0) {
+            System.err.println("PM Create Session Failed");
+            return -1;
+        }
+
+        int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
+
+        if (writeExitCode == 0) {
+            return commitInstallSession(sessionId);
+        } else {
+            return -1;
+        }
+    }
+
+    private static long writePatchToStream(RandomAccessFile oldData, InputStream patchData,
+        OutputStream outputStream) throws IOException, PatchFormatException {
+        long newSize = readPatchHeader(patchData);
+        long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, outputStream);
+        outputStream.flush();
+        if (bytesWritten != newSize) {
+            throw new PatchFormatException(String.format(
+                "output size mismatch (expected %ld but wrote %ld)", newSize, bytesWritten));
+        }
+        return bytesWritten;
+    }
+
+    private static long readPatchHeader(InputStream patchData)
+        throws IOException, PatchFormatException {
+        byte[] signatureBuffer = new byte[PatchUtils.SIGNATURE.length()];
+        try {
+            PatchUtils.readFully(patchData, signatureBuffer, 0, signatureBuffer.length);
+        } catch (IOException e) {
+            throw new PatchFormatException("truncated signature");
+        }
+
+        String signature = new String(signatureBuffer, 0, signatureBuffer.length, "US-ASCII");
+        if (!PatchUtils.SIGNATURE.equals(signature)) {
+            throw new PatchFormatException("bad signature");
+        }
+
+        long newSize = PatchUtils.readBsdiffLong(patchData);
+        if (newSize < 0 || newSize > Integer.MAX_VALUE) {
+            throw new PatchFormatException("bad newSize");
+        }
+
+        return newSize;
+    }
+
+    // Note that this function assumes patchData has been seek'ed to the start of the delta stream
+    // (i.e. the signature has already been read by readPatchHeader). For a stream that points to the
+    // start of a patch file call writePatchToStream
+    private static long writePatchedDataToStream(RandomAccessFile oldData, long newSize,
+        InputStream patchData, OutputStream outputStream) throws IOException {
+        long newDataBytesWritten = 0;
+        byte[] buffer = new byte[BUFFER_SIZE];
+
+        while (newDataBytesWritten < newSize) {
+            long copyLen = PatchUtils.readFormattedLong(patchData);
+            if (copyLen > 0) {
+                PatchUtils.pipe(patchData, outputStream, buffer, (int) copyLen);
+            }
+
+            long oldDataOffset = PatchUtils.readFormattedLong(patchData);
+            long oldDataLen = PatchUtils.readFormattedLong(patchData);
+            oldData.seek(oldDataOffset);
+            if (oldDataLen > 0) {
+                PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
+            }
+
+            newDataBytesWritten += copyLen + oldDataLen;
+        }
+
+        return newDataBytesWritten;
+    }
+
+    private static int writePatchedDataToSession(RandomAccessFile oldData, InputStream patchData, int sessionId)
+            throws IOException, PatchFormatException {
+        try {
+            Process p;
+            long newSize = readPatchHeader(patchData);
+            StringBuilder commandBuilder = new StringBuilder();
+            commandBuilder.append(String.format("pm install-write -S %d %d -- -", newSize, sessionId));
+
+            String command = commandBuilder.toString();
+            p = Runtime.getRuntime().exec(command);
+
+            OutputStream sessionOutputStream = p.getOutputStream();
+            long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, sessionOutputStream);
+            sessionOutputStream.flush();
+            p.waitFor();
+            if (bytesWritten != newSize) {
+                throw new PatchFormatException(
+                        String.format("output size mismatch (expected %d but wrote %)", newSize, bytesWritten));
+            }
+            return p.exitValue();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+
+        return -1;
+    }
+}
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
new file mode 100644
index 0000000..f0655f3
--- /dev/null
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fastdeploy;
+
+class PatchFormatException extends Exception {
+    /**
+     * Constructs a new exception with the specified message.
+     * @param message the message
+     */
+    public PatchFormatException(String message) { super(message); }
+
+    /**
+     * Constructs a new exception with the specified message and cause.
+     * @param message the message
+     * @param cause the cause of the error
+     */
+    public PatchFormatException(String message, Throwable cause) {
+        super(message);
+        initCause(cause);
+    }
+}
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
new file mode 100644
index 0000000..f0f00e1
--- /dev/null
+++ b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fastdeploy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import com.android.tools.build.apkzlib.zip.ZFile;
+import com.android.tools.build.apkzlib.zip.ZFileOptions;
+import com.android.tools.build.apkzlib.zip.StoredEntry;
+import com.android.tools.build.apkzlib.zip.StoredEntryType;
+import com.android.tools.build.apkzlib.zip.CentralDirectoryHeaderCompressInfo;
+import com.android.tools.build.apkzlib.zip.CentralDirectoryHeader;
+
+import com.android.fastdeploy.APKMetaData;
+import com.android.fastdeploy.APKEntry;
+
+class PatchUtils {
+    private static final long NEGATIVE_MASK = 1L << 63;
+    private static final long NEGATIVE_LONG_SIGN_MASK = 1L << 63;
+    public static final String SIGNATURE = "HAMADI/IHD";
+
+    private static long getOffsetFromEntry(StoredEntry entry) {
+        return entry.getCentralDirectoryHeader().getOffset() + entry.getLocalHeaderSize();
+    }
+
+    public static APKMetaData getAPKMetaData(File apkFile) throws IOException {
+        APKMetaData.Builder apkEntriesBuilder = APKMetaData.newBuilder();
+        ZFileOptions options = new ZFileOptions();
+        ZFile zFile = new ZFile(apkFile, options);
+
+        ArrayList<StoredEntry> metaDataEntries = new ArrayList<StoredEntry>();
+
+        for (StoredEntry entry : zFile.entries()) {
+            if (entry.getType() != StoredEntryType.FILE) {
+                continue;
+            }
+            metaDataEntries.add(entry);
+        }
+
+        Collections.sort(metaDataEntries, new Comparator<StoredEntry>() {
+            private long getOffsetFromEntry(StoredEntry entry) {
+                return PatchUtils.getOffsetFromEntry(entry);
+            }
+
+            @Override
+            public int compare(StoredEntry lhs, StoredEntry rhs) {
+                // -1 - less than, 1 - greater than, 0 - equal, all inversed for descending
+                return Long.compare(getOffsetFromEntry(lhs), getOffsetFromEntry(rhs));
+            }
+        });
+
+        for (StoredEntry entry : metaDataEntries) {
+            CentralDirectoryHeader cdh = entry.getCentralDirectoryHeader();
+            CentralDirectoryHeaderCompressInfo cdhci = cdh.getCompressionInfoWithWait();
+
+            APKEntry.Builder entryBuilder = APKEntry.newBuilder();
+            entryBuilder.setCrc32(cdh.getCrc32());
+            entryBuilder.setFileName(cdh.getName());
+            entryBuilder.setCompressedSize(cdhci.getCompressedSize());
+            entryBuilder.setUncompressedSize(cdh.getUncompressedSize());
+            entryBuilder.setDataOffset(getOffsetFromEntry(entry));
+
+            apkEntriesBuilder.addEntries(entryBuilder);
+            apkEntriesBuilder.build();
+        }
+        return apkEntriesBuilder.build();
+    }
+
+    /**
+     * Writes a 64-bit signed integer to the specified {@link OutputStream}. The least significant
+     * byte is written first and the most significant byte is written last.
+     * @param value the value to write
+     * @param outputStream the stream to write to
+     */
+    static void writeFormattedLong(final long value, OutputStream outputStream) throws IOException {
+        long y = value;
+        if (y < 0) {
+            y = (-y) | NEGATIVE_MASK;
+        }
+
+        for (int i = 0; i < 8; ++i) {
+            outputStream.write((byte) (y & 0xff));
+            y >>>= 8;
+        }
+    }
+
+    /**
+     * Reads a 64-bit signed integer written by {@link #writeFormattedLong(long, OutputStream)} from
+     * the specified {@link InputStream}.
+     * @param inputStream the stream to read from
+     */
+    static long readFormattedLong(InputStream inputStream) throws IOException {
+        long result = 0;
+        for (int bitshift = 0; bitshift < 64; bitshift += 8) {
+            result |= ((long) inputStream.read()) << bitshift;
+        }
+
+        if ((result - NEGATIVE_MASK) > 0) {
+            result = (result & ~NEGATIVE_MASK) * -1;
+        }
+        return result;
+    }
+
+    static final long readBsdiffLong(InputStream in) throws PatchFormatException, IOException {
+        long result = 0;
+        for (int bitshift = 0; bitshift < 64; bitshift += 8) {
+            result |= ((long) in.read()) << bitshift;
+        }
+
+        if (result == NEGATIVE_LONG_SIGN_MASK) {
+            // "Negative zero", which is valid in signed-magnitude format.
+            // NB: No sane patch generator should ever produce such a value.
+            throw new PatchFormatException("read negative zero");
+        }
+
+        if ((result & NEGATIVE_LONG_SIGN_MASK) != 0) {
+            result = -(result & ~NEGATIVE_LONG_SIGN_MASK);
+        }
+
+        return result;
+    }
+
+    static void readFully(final InputStream in, final byte[] destination, final int startAt,
+        final int numBytes) throws IOException {
+        int numRead = 0;
+        while (numRead < numBytes) {
+            int readNow = in.read(destination, startAt + numRead, numBytes - numRead);
+            if (readNow == -1) {
+                throw new IOException("truncated input stream");
+            }
+            numRead += readNow;
+        }
+    }
+
+    static void pipe(final InputStream in, final OutputStream out, final byte[] buffer,
+        long copyLength) throws IOException {
+        while (copyLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) copyLength);
+            readFully(in, buffer, 0, maxCopy);
+            out.write(buffer, 0, maxCopy);
+            copyLength -= maxCopy;
+        }
+    }
+
+    static void pipe(final RandomAccessFile in, final OutputStream out, final byte[] buffer,
+        long copyLength) throws IOException {
+        while (copyLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) copyLength);
+            in.readFully(buffer, 0, maxCopy);
+            out.write(buffer, 0, maxCopy);
+            copyLength -= maxCopy;
+        }
+    }
+
+    static void fill(byte value, final OutputStream out, final byte[] buffer, long fillLength)
+        throws IOException {
+        while (fillLength > 0) {
+            int maxCopy = Math.min(buffer.length, (int) fillLength);
+            Arrays.fill(buffer, 0, maxCopy, value);
+            out.write(buffer, 0, maxCopy);
+            fillLength -= maxCopy;
+        }
+    }
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/manifest.txt b/adb/fastdeploy/deploypatchgenerator/manifest.txt
new file mode 100644
index 0000000..5c00505
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.fastdeploy.DeployPatchGenerator
diff --git a/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
new file mode 100644
index 0000000..5577364
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/src/com/android/fastdeploy/DeployPatchGenerator.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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);
+            if (verbose) {
+                sb = new StringBuilder();
+                for (APKEntry entry : deviceZipEntries) {
+                    APKEntryToString(entry, sb);
+                }
+                System.err.println("Device Entries (" + deviceZipEntries.size() + ")");
+                System.err.println(sb.toString());
+            }
+
+            List<APKEntry> hostFileEntries = PatchUtils.getAPKMetaData(hostFile).getEntriesList();
+            if (verbose) {
+                sb = new StringBuilder();
+                for (APKEntry entry : hostFileEntries) {
+                    APKEntryToString(entry, sb);
+                }
+                System.err.println("Host Entries (" + hostFileEntries.size() + ")");
+                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()) {
+                    identicalContents.add(new SimpleEntry(deviceZipEntry, hostZipEntry));
+                }
+            }
+        }
+
+        Collections.sort(identicalContents, new Comparator<SimpleEntry<APKEntry, APKEntry>>() {
+            @Override
+            public int compare(
+                SimpleEntry<APKEntry, APKEntry> p1, SimpleEntry<APKEntry, APKEntry> p2) {
+                return Long.compare(p1.getValue().getDataOffset(), p2.getValue().getDataOffset());
+            }
+        });
+
+        return identicalContents;
+    }
+
+    private static void reportIdenticalContents(
+        List<SimpleEntry<APKEntry, APKEntry>> identicalContentsEntrySet, File hostFile)
+        throws IOException {
+        long totalEqualBytes = 0;
+        int totalEqualFiles = 0;
+        for (SimpleEntry<APKEntry, APKEntry> entries : identicalContentsEntrySet) {
+            APKEntry hostAPKEntry = entries.getValue();
+            totalEqualBytes += hostAPKEntry.getCompressedSize();
+            totalEqualFiles++;
+        }
+
+        float savingPercent = (float) (totalEqualBytes * 100) / hostFile.length();
+
+        System.err.println("Detected " + totalEqualFiles + " equal APK entries");
+        System.err.println(totalEqualBytes + " bytes are equal out of " + hostFile.length() + " ("
+            + savingPercent + "%)");
+    }
+
+    static void createPatch(List<SimpleEntry<APKEntry, APKEntry>> zipEntrySimpleEntrys,
+        File hostFile, OutputStream patchStream) throws IOException, PatchFormatException {
+        FileInputStream hostFileInputStream = new FileInputStream(hostFile);
+
+        patchStream.write(PatchUtils.SIGNATURE.getBytes(StandardCharsets.US_ASCII));
+        PatchUtils.writeFormattedLong(hostFile.length(), patchStream);
+
+        byte[] buffer = new byte[BUFFER_SIZE];
+        long totalBytesWritten = 0;
+        Iterator<SimpleEntry<APKEntry, APKEntry>> entrySimpleEntryIterator =
+            zipEntrySimpleEntrys.iterator();
+        while (entrySimpleEntryIterator.hasNext()) {
+            SimpleEntry<APKEntry, APKEntry> entrySimpleEntry = entrySimpleEntryIterator.next();
+            APKEntry deviceAPKEntry = entrySimpleEntry.getKey();
+            APKEntry hostAPKEntry = entrySimpleEntry.getValue();
+
+            long newDataLen = hostAPKEntry.getDataOffset() - totalBytesWritten;
+            long oldDataOffset = deviceAPKEntry.getDataOffset();
+            long oldDataLen = deviceAPKEntry.getCompressedSize();
+
+            PatchUtils.writeFormattedLong(newDataLen, patchStream);
+            PatchUtils.pipe(hostFileInputStream, patchStream, buffer, newDataLen);
+            PatchUtils.writeFormattedLong(oldDataOffset, patchStream);
+            PatchUtils.writeFormattedLong(oldDataLen, patchStream);
+
+            long skip = hostFileInputStream.skip(oldDataLen);
+            if (skip != oldDataLen) {
+                throw new PatchFormatException("skip error: attempted to skip " + oldDataLen
+                    + " bytes but return code was " + skip);
+            }
+            totalBytesWritten += oldDataLen + newDataLen;
+        }
+        long remainderLen = hostFile.length() - totalBytesWritten;
+        PatchUtils.writeFormattedLong(remainderLen, patchStream);
+        PatchUtils.pipe(hostFileInputStream, patchStream, buffer, remainderLen);
+        PatchUtils.writeFormattedLong(0, patchStream);
+        PatchUtils.writeFormattedLong(0, patchStream);
+        patchStream.flush();
+    }
+}
diff --git a/adb/fastdeploy/proto/ApkEntry.proto b/adb/fastdeploy/proto/ApkEntry.proto
new file mode 100644
index 0000000..9460d15
--- /dev/null
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -0,0 +1,18 @@
+syntax = "proto2";
+
+package com.android.fastdeploy;
+
+option java_package = "com.android.fastdeploy";
+option java_multiple_files = true;
+
+message APKEntry {
+    required int64 crc32 = 1;
+    required string fileName = 2;
+    required int64 dataOffset = 3;
+    required int64 compressedSize = 4;
+    required int64 uncompressedSize = 5;
+}
+
+message APKMetaData {
+    repeated APKEntry entries = 1;
+}
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f2911e0..be0bdd0 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -72,12 +72,7 @@
     return c == '\\' || c == '/';
 }
 
-static __inline__ int adb_thread_setname(const std::string& name) {
-    // TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
-    // the thread name in Windows. Unfortunately, it only works during debugging, but
-    // our build process doesn't generate PDB files needed for debugging.
-    return 0;
-}
+extern int adb_thread_setname(const std::string& name);
 
 static __inline__ void  close_on_exec(int  fd)
 {
@@ -129,6 +124,13 @@
 #undef   write
 #define  write  ___xxx_write
 
+// See the comments for the !defined(_WIN32) version of unix_lseek().
+static __inline__ int unix_lseek(int fd, int pos, int where) {
+    return lseek(fd, pos, where);
+}
+#undef lseek
+#define lseek ___xxx_lseek
+
 // See the comments for the !defined(_WIN32) version of adb_open_mode().
 static __inline__ int  adb_open_mode(const char* path, int options, int mode)
 {
@@ -493,21 +495,7 @@
     return _fd_set_error_str(socket_local_server(name, namespace_id, type), error);
 }
 
-inline int network_connect(const std::string& host, int port, int type,
-                           int timeout, std::string* error) {
-  int getaddrinfo_error = 0;
-  int fd = socket_network_client_timeout(host.c_str(), port, type, timeout,
-                                         &getaddrinfo_error);
-  if (fd != -1) {
-    return fd;
-  }
-  if (getaddrinfo_error != 0) {
-    *error = gai_strerror(getaddrinfo_error);
-  } else {
-    *error = strerror(errno);
-  }
-  return -1;
-}
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error);
 
 static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
 {
@@ -537,6 +525,7 @@
 // via _setmode()).
 #define  unix_read   adb_read
 #define  unix_write  adb_write
+#define unix_lseek adb_lseek
 #define  unix_close  adb_close
 
 static __inline__ int adb_thread_setname(const std::string& name) {
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index ecd1fd2..33ddb4e 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -17,11 +17,15 @@
 #include "sysdeps/network.h"
 
 #include <errno.h>
+#include <netdb.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
 
 #include <string>
 
+#include <android-base/logging.h>
+#include <cutils/sockets.h>
+
 #include "adb_unique_fd.h"
 
 static void set_error(std::string* error) {
@@ -124,3 +128,19 @@
     }
     return rc;
 }
+
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
+    int getaddrinfo_error = 0;
+    int fd = socket_network_client_timeout(host.c_str(), port, type, timeout, &getaddrinfo_error);
+    if (fd != -1) {
+        return fd;
+    }
+    if (getaddrinfo_error != 0) {
+        *error = gai_strerror(getaddrinfo_error);
+        LOG(WARNING) << "failed to resolve host '" << host << "': " << *error;
+    } else {
+        *error = strerror(errno);
+        LOG(WARNING) << "failed to connect to '" << host << "': " << *error;
+    }
+    return -1;
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index c94d13f..026dd1c 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2742,3 +2742,26 @@
 
     return buf;
 }
+
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
+
+// Based on PlatformThread::SetName() from
+// https://cs.chromium.org/chromium/src/base/threading/platform_thread_win.cc
+int adb_thread_setname(const std::string& name) {
+    // The SetThreadDescription API works even if no debugger is attached.
+    auto set_thread_description_func = reinterpret_cast<SetThreadDescription>(
+            ::GetProcAddress(::GetModuleHandleW(L"Kernel32.dll"), "SetThreadDescription"));
+    if (set_thread_description_func) {
+        std::wstring name_wide;
+        if (!android::base::UTF8ToWide(name.c_str(), &name_wide)) {
+            return errno;
+        }
+        set_thread_description_func(::GetCurrentThread(), name_wide.c_str());
+    }
+
+    // Don't use the thread naming SEH exception because we're compiled with -fno-exceptions.
+    // https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2017
+
+    return 0;
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 86c13d0..7e73818 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -27,6 +27,7 @@
 import socket
 import struct
 import subprocess
+import sys
 import threading
 import time
 import unittest
@@ -129,10 +130,19 @@
     subprocess.check_output(["adb", "-P", str(port), "kill-server"],
                             stderr=subprocess.STDOUT)
     read_pipe, write_pipe = os.pipe()
-    os.set_inheritable(write_pipe, True)
+
+    if sys.platform == "win32":
+        import msvcrt
+        write_handle = msvcrt.get_osfhandle(write_pipe)
+        os.set_handle_inheritable(write_handle, True)
+        reply_fd = str(write_handle)
+    else:
+        os.set_inheritable(write_pipe, True)
+        reply_fd = str(write_pipe)
+
     proc = subprocess.Popen(["adb", "-L", "tcp:localhost:{}".format(port),
                              "fork-server", "server",
-                             "--reply-fd", str(write_pipe)], close_fds=False)
+                             "--reply-fd", reply_fd], close_fds=False)
     try:
         os.close(write_pipe)
         greeting = os.read(read_pipe, 1024)
@@ -480,6 +490,58 @@
                 self.assertEqual(_devices(server_port), [])
 
 
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class PowerTest(unittest.TestCase):
+    def test_resume_usb_kick(self):
+        """Resuming from sleep/hibernate should kick USB devices."""
+        try:
+            usb_serial = subprocess.check_output(["adb", "-d", "get-serialno"]).strip()
+        except subprocess.CalledProcessError:
+            # If there are multiple USB devices, we don't have a way to check whether the selected
+            # device is USB.
+            raise unittest.SkipTest('requires single USB device')
+
+        try:
+            serial = subprocess.check_output(["adb", "get-serialno"]).strip()
+        except subprocess.CalledProcessError:
+            # Did you forget to select a device with $ANDROID_SERIAL?
+            raise unittest.SkipTest('requires $ANDROID_SERIAL set to a USB device')
+
+        # Test only works with USB devices because adb _power_notification_thread does not kick
+        # non-USB devices on resume event.
+        if serial != usb_serial:
+            raise unittest.SkipTest('requires USB device')
+
+        # Run an adb shell command in the background that takes a while to complete.
+        proc = subprocess.Popen(['adb', 'shell', 'sleep', '5'])
+
+        # Wait for startup of adb server's _power_notification_thread.
+        time.sleep(0.1)
+
+        # Simulate resuming from sleep/hibernation by sending Windows message.
+        import ctypes
+        from ctypes import wintypes
+        HWND_BROADCAST = 0xffff
+        WM_POWERBROADCAST = 0x218
+        PBT_APMRESUMEAUTOMATIC = 0x12
+
+        PostMessageW = ctypes.windll.user32.PostMessageW
+        PostMessageW.restype = wintypes.BOOL
+        PostMessageW.argtypes = (wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)
+        result = PostMessageW(HWND_BROADCAST, WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC, 0)
+        if not result:
+            raise ctypes.WinError()
+
+        # Wait for connection to adb shell to be broken by _power_notification_thread detecting the
+        # Windows message.
+        start = time.time()
+        proc.wait()
+        end = time.time()
+
+        # If the power event was detected, the adb shell command should be broken very quickly.
+        self.assertLess(end - start, 2)
+
+
 def main():
     """Main entrypoint."""
     random.seed(0)
diff --git a/adb/test_device.py b/adb/test_device.py
index 42aadc4..9f45115 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1302,6 +1302,237 @@
                 self.assertEqual(length, len(stdout) - 4)
                 self.assertEqual(stdout, "\0" * length + "foo\n")
 
+    def test_zero_packet(self):
+        """Test for http://b/113070258
+
+        Make sure that we don't blow up when sending USB transfers that line up
+        exactly with the USB packet size.
+        """
+
+        local_port = int(self.device.forward("tcp:0", "tcp:12345"))
+        try:
+            for size in [512, 1024]:
+                def listener():
+                    cmd = ["echo foo | nc -l -p 12345; echo done"]
+                    rc, stdout, stderr = self.device.shell_nocheck(cmd)
+
+                thread = threading.Thread(target=listener)
+                thread.start()
+
+                # Wait a bit to let the shell command start.
+                time.sleep(0.25)
+
+                sock = socket.create_connection(("localhost", local_port))
+                with contextlib.closing(sock):
+                    bytesWritten = sock.send("a" * size)
+                    self.assertEqual(size, bytesWritten)
+                    readBytes = sock.recv(4096)
+                    self.assertEqual("foo\n", readBytes)
+
+                thread.join()
+        finally:
+            self.device.forward_remove("tcp:{}".format(local_port))
+
+
+if sys.platform == "win32":
+    # From https://stackoverflow.com/a/38749458
+    import os
+    import contextlib
+    import msvcrt
+    import ctypes
+    from ctypes import wintypes
+
+    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
+
+    GENERIC_READ  = 0x80000000
+    GENERIC_WRITE = 0x40000000
+    FILE_SHARE_READ  = 1
+    FILE_SHARE_WRITE = 2
+    CONSOLE_TEXTMODE_BUFFER = 1
+    INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
+    STD_OUTPUT_HANDLE = wintypes.DWORD(-11)
+    STD_ERROR_HANDLE = wintypes.DWORD(-12)
+
+    def _check_zero(result, func, args):
+        if not result:
+            raise ctypes.WinError(ctypes.get_last_error())
+        return args
+
+    def _check_invalid(result, func, args):
+        if result == INVALID_HANDLE_VALUE:
+            raise ctypes.WinError(ctypes.get_last_error())
+        return args
+
+    if not hasattr(wintypes, 'LPDWORD'): # Python 2
+        wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD)
+        wintypes.PSMALL_RECT = ctypes.POINTER(wintypes.SMALL_RECT)
+
+    class COORD(ctypes.Structure):
+        _fields_ = (('X', wintypes.SHORT),
+                    ('Y', wintypes.SHORT))
+
+    class CONSOLE_SCREEN_BUFFER_INFOEX(ctypes.Structure):
+        _fields_ = (('cbSize',               wintypes.ULONG),
+                    ('dwSize',               COORD),
+                    ('dwCursorPosition',     COORD),
+                    ('wAttributes',          wintypes.WORD),
+                    ('srWindow',             wintypes.SMALL_RECT),
+                    ('dwMaximumWindowSize',  COORD),
+                    ('wPopupAttributes',     wintypes.WORD),
+                    ('bFullscreenSupported', wintypes.BOOL),
+                    ('ColorTable',           wintypes.DWORD * 16))
+        def __init__(self, *args, **kwds):
+            super(CONSOLE_SCREEN_BUFFER_INFOEX, self).__init__(
+                    *args, **kwds)
+            self.cbSize = ctypes.sizeof(self)
+
+    PCONSOLE_SCREEN_BUFFER_INFOEX = ctypes.POINTER(
+                                        CONSOLE_SCREEN_BUFFER_INFOEX)
+    LPSECURITY_ATTRIBUTES = wintypes.LPVOID
+
+    kernel32.GetStdHandle.errcheck = _check_invalid
+    kernel32.GetStdHandle.restype = wintypes.HANDLE
+    kernel32.GetStdHandle.argtypes = (
+        wintypes.DWORD,) # _In_ nStdHandle
+
+    kernel32.CreateConsoleScreenBuffer.errcheck = _check_invalid
+    kernel32.CreateConsoleScreenBuffer.restype = wintypes.HANDLE
+    kernel32.CreateConsoleScreenBuffer.argtypes = (
+        wintypes.DWORD,        # _In_       dwDesiredAccess
+        wintypes.DWORD,        # _In_       dwShareMode
+        LPSECURITY_ATTRIBUTES, # _In_opt_   lpSecurityAttributes
+        wintypes.DWORD,        # _In_       dwFlags
+        wintypes.LPVOID)       # _Reserved_ lpScreenBufferData
+
+    kernel32.GetConsoleScreenBufferInfoEx.errcheck = _check_zero
+    kernel32.GetConsoleScreenBufferInfoEx.argtypes = (
+        wintypes.HANDLE,               # _In_  hConsoleOutput
+        PCONSOLE_SCREEN_BUFFER_INFOEX) # _Out_ lpConsoleScreenBufferInfo
+
+    kernel32.SetConsoleScreenBufferInfoEx.errcheck = _check_zero
+    kernel32.SetConsoleScreenBufferInfoEx.argtypes = (
+        wintypes.HANDLE,               # _In_  hConsoleOutput
+        PCONSOLE_SCREEN_BUFFER_INFOEX) # _In_  lpConsoleScreenBufferInfo
+
+    kernel32.SetConsoleWindowInfo.errcheck = _check_zero
+    kernel32.SetConsoleWindowInfo.argtypes = (
+        wintypes.HANDLE,      # _In_ hConsoleOutput
+        wintypes.BOOL,        # _In_ bAbsolute
+        wintypes.PSMALL_RECT) # _In_ lpConsoleWindow
+
+    kernel32.FillConsoleOutputCharacterW.errcheck = _check_zero
+    kernel32.FillConsoleOutputCharacterW.argtypes = (
+        wintypes.HANDLE,  # _In_  hConsoleOutput
+        wintypes.WCHAR,   # _In_  cCharacter
+        wintypes.DWORD,   # _In_  nLength
+        COORD,            # _In_  dwWriteCoord
+        wintypes.LPDWORD) # _Out_ lpNumberOfCharsWritten
+
+    kernel32.ReadConsoleOutputCharacterW.errcheck = _check_zero
+    kernel32.ReadConsoleOutputCharacterW.argtypes = (
+        wintypes.HANDLE,  # _In_  hConsoleOutput
+        wintypes.LPWSTR,  # _Out_ lpCharacter
+        wintypes.DWORD,   # _In_  nLength
+        COORD,            # _In_  dwReadCoord
+        wintypes.LPDWORD) # _Out_ lpNumberOfCharsRead
+
+    @contextlib.contextmanager
+    def allocate_console():
+        allocated = kernel32.AllocConsole()
+        try:
+            yield allocated
+        finally:
+            if allocated:
+                kernel32.FreeConsole()
+
+    @contextlib.contextmanager
+    def console_screen(ncols=None, nrows=None):
+        info = CONSOLE_SCREEN_BUFFER_INFOEX()
+        new_info = CONSOLE_SCREEN_BUFFER_INFOEX()
+        nwritten = (wintypes.DWORD * 1)()
+        hStdOut = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
+        kernel32.GetConsoleScreenBufferInfoEx(
+               hStdOut, ctypes.byref(info))
+        if ncols is None:
+            ncols = info.dwSize.X
+        if nrows is None:
+            nrows = info.dwSize.Y
+        elif nrows > 9999:
+            raise ValueError('nrows must be 9999 or less')
+        fd_screen = None
+        hScreen = kernel32.CreateConsoleScreenBuffer(
+                    GENERIC_READ | GENERIC_WRITE,
+                    FILE_SHARE_READ | FILE_SHARE_WRITE,
+                    None, CONSOLE_TEXTMODE_BUFFER, None)
+        try:
+            fd_screen = msvcrt.open_osfhandle(
+                            hScreen, os.O_RDWR | os.O_BINARY)
+            kernel32.GetConsoleScreenBufferInfoEx(
+                   hScreen, ctypes.byref(new_info))
+            new_info.dwSize = COORD(ncols, nrows)
+            new_info.srWindow = wintypes.SMALL_RECT(
+                    Left=0, Top=0, Right=(ncols - 1),
+                    Bottom=(info.srWindow.Bottom - info.srWindow.Top))
+            kernel32.SetConsoleScreenBufferInfoEx(
+                    hScreen, ctypes.byref(new_info))
+            kernel32.SetConsoleWindowInfo(hScreen, True,
+                    ctypes.byref(new_info.srWindow))
+            kernel32.FillConsoleOutputCharacterW(
+                    hScreen, u'\0', ncols * nrows, COORD(0,0), nwritten)
+            kernel32.SetConsoleActiveScreenBuffer(hScreen)
+            try:
+                yield fd_screen
+            finally:
+                kernel32.SetConsoleScreenBufferInfoEx(
+                    hStdOut, ctypes.byref(info))
+                kernel32.SetConsoleWindowInfo(hStdOut, True,
+                        ctypes.byref(info.srWindow))
+                kernel32.SetConsoleActiveScreenBuffer(hStdOut)
+        finally:
+            if fd_screen is not None:
+                os.close(fd_screen)
+            else:
+                kernel32.CloseHandle(hScreen)
+
+    def read_screen(fd):
+        hScreen = msvcrt.get_osfhandle(fd)
+        csbi = CONSOLE_SCREEN_BUFFER_INFOEX()
+        kernel32.GetConsoleScreenBufferInfoEx(
+            hScreen, ctypes.byref(csbi))
+        ncols = csbi.dwSize.X
+        pos = csbi.dwCursorPosition
+        length = ncols * pos.Y + pos.X + 1
+        buf = (ctypes.c_wchar * length)()
+        n = (wintypes.DWORD * 1)()
+        kernel32.ReadConsoleOutputCharacterW(
+            hScreen, buf, length, COORD(0,0), n)
+        lines = [buf[i:i+ncols].rstrip(u'\0')
+                    for i in range(0, n[0], ncols)]
+        return u'\n'.join(lines)
+
+@unittest.skipUnless(sys.platform == "win32", "requires Windows")
+class WindowsConsoleTest(DeviceTest):
+    def test_unicode_output(self):
+        """Test Unicode command line parameters and Unicode console window output.
+
+        Bug: https://issuetracker.google.com/issues/111972753
+        """
+        # If we don't have a console window, allocate one. This isn't necessary if we're already
+        # being run from a console window, which is typical.
+        with allocate_console() as allocated_console:
+            # Create a temporary console buffer and switch to it. We could also pass a parameter of
+            # ncols=len(unicode_string), but it causes the window to flash as it is resized and
+            # likely unnecessary given the typical console window size.
+            with console_screen(nrows=1000) as screen:
+                unicode_string = u'로보카 폴리'
+                # Run adb and allow it to detect that stdout is a console, not a pipe, by using
+                # device.shell_popen() which does not use a pipe, unlike device.shell().
+                process = self.device.shell_popen(['echo', '"' + unicode_string + '"'])
+                process.wait()
+                # Read what was written by adb to the temporary console buffer.
+                console_output = read_screen(screen)
+                self.assertEqual(unicode_string, console_output)
+
 
 def main():
     random.seed(0)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 332e0f8..95df490 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -220,25 +220,34 @@
         }
         D("attempting to reconnect %s", attempt.transport->serial.c_str());
 
-        if (!attempt.transport->Reconnect()) {
-            D("attempting to reconnect %s failed.", attempt.transport->serial.c_str());
-            if (attempt.attempts_left == 0) {
-                D("transport %s exceeded the number of retry attempts. giving up on it.",
-                  attempt.transport->serial.c_str());
-                remove_transport(attempt.transport);
+        switch (attempt.transport->Reconnect()) {
+            case ReconnectResult::Retry: {
+                D("attempting to reconnect %s failed.", attempt.transport->serial.c_str());
+                if (attempt.attempts_left == 0) {
+                    D("transport %s exceeded the number of retry attempts. giving up on it.",
+                      attempt.transport->serial.c_str());
+                    remove_transport(attempt.transport);
+                    continue;
+                }
+
+                std::lock_guard<std::mutex> lock(reconnect_mutex_);
+                reconnect_queue_.emplace(ReconnectAttempt{
+                        attempt.transport,
+                        std::chrono::steady_clock::now() + ReconnectHandler::kDefaultTimeout,
+                        attempt.attempts_left - 1});
                 continue;
             }
 
-            std::lock_guard<std::mutex> lock(reconnect_mutex_);
-            reconnect_queue_.emplace(ReconnectAttempt{
-                    attempt.transport,
-                    std::chrono::steady_clock::now() + ReconnectHandler::kDefaultTimeout,
-                    attempt.attempts_left - 1});
-            continue;
-        }
+            case ReconnectResult::Success:
+                D("reconnection to %s succeeded.", attempt.transport->serial.c_str());
+                register_transport(attempt.transport);
+                continue;
 
-        D("reconnection to %s succeeded.", attempt.transport->serial.c_str());
-        register_transport(attempt.transport);
+            case ReconnectResult::Abort:
+                D("cancelling reconnection attempt to %s.", attempt.transport->serial.c_str());
+                remove_transport(attempt.transport);
+                continue;
+        }
     }
 }
 
@@ -1031,12 +1040,6 @@
     return max_payload;
 }
 
-namespace {
-
-constexpr char kFeatureStringDelimiter = ',';
-
-}  // namespace
-
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
@@ -1050,7 +1053,7 @@
 }
 
 std::string FeatureSetToString(const FeatureSet& features) {
-    return android::base::Join(features, kFeatureStringDelimiter);
+    return android::base::Join(features, ',');
 }
 
 FeatureSet StringToFeatureSet(const std::string& features_string) {
@@ -1058,7 +1061,7 @@
         return FeatureSet();
     }
 
-    auto names = android::base::Split(features_string, {kFeatureStringDelimiter});
+    auto names = android::base::Split(features_string, ",");
     return FeatureSet(names.begin(), names.end());
 }
 
@@ -1128,7 +1131,7 @@
     connection_waitable_->SetConnectionEstablished(success);
 }
 
-bool atransport::Reconnect() {
+ReconnectResult atransport::Reconnect() {
     return reconnect_(this);
 }
 
diff --git a/adb/transport.h b/adb/transport.h
index f362f24..f854ce5 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -193,6 +193,12 @@
     DISALLOW_COPY_AND_ASSIGN(ConnectionWaitable);
 };
 
+enum class ReconnectResult {
+    Retry,
+    Success,
+    Abort,
+};
+
 class atransport {
   public:
     // TODO(danalbert): We expose waaaaaaay too much stuff because this was
@@ -200,7 +206,7 @@
     // class in one go is a very large change. Given how bad our testing is,
     // it's better to do this piece by piece.
 
-    using ReconnectCallback = std::function<bool(atransport*)>;
+    using ReconnectCallback = std::function<ReconnectResult(atransport*)>;
 
     atransport(ReconnectCallback reconnect, ConnectionState state)
         : id(NextTransportId()),
@@ -215,7 +221,7 @@
         max_payload = MAX_PAYLOAD;
     }
     atransport(ConnectionState state = kCsOffline)
-        : atransport([](atransport*) { return false; }, state) {}
+        : atransport([](atransport*) { return ReconnectResult::Abort; }, state) {}
     virtual ~atransport();
 
     int Write(apacket* p);
@@ -295,9 +301,8 @@
     // Gets a shared reference to the ConnectionWaitable.
     std::shared_ptr<ConnectionWaitable> connection_waitable() { return connection_waitable_; }
 
-    // Attempts to reconnect with the underlying Connection. Returns true if the
-    // reconnection attempt succeeded.
-    bool Reconnect();
+    // Attempts to reconnect with the underlying Connection.
+    ReconnectResult Reconnect();
 
   private:
     std::atomic<bool> kicked_;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 1431252..8353d89 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -74,6 +74,7 @@
     std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
     if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
+        D("failed to parse address: '%s'", address.c_str());
         return std::make_tuple(unique_fd(), port, serial);
     }
 
@@ -103,6 +104,7 @@
         return;
     }
 
+    D("connection requested to '%s'", address.c_str());
     unique_fd fd;
     int port;
     std::string serial;
@@ -115,14 +117,15 @@
         std::tie(fd, port, serial) = tcp_connect(address, &response);
         if (fd == -1) {
             D("reconnect failed: %s", response.c_str());
-            return false;
+            return ReconnectResult::Retry;
         }
 
         // This invokes the part of register_socket_transport() that needs to be
         // invoked if the atransport* has already been setup. This eventually
         // calls atransport->SetConnection() with a newly created Connection*
         // that will in turn send the CNXN packet.
-        return init_socket_transport(t, std::move(fd), port, 0) >= 0;
+        return init_socket_transport(t, std::move(fd), port, 0) >= 0 ? ReconnectResult::Success
+                                                                     : ReconnectResult::Retry;
     };
 
     int error;
@@ -164,7 +167,7 @@
         disable_tcp_nagle(fd.get());
         std::string serial = getEmulatorSerialString(console_port);
         if (register_socket_transport(std::move(fd), std::move(serial), adb_port, 1,
-                                      [](atransport*) { return false; })) {
+                                      [](atransport*) { return ReconnectResult::Abort; })) {
             return 0;
         }
     }
@@ -184,8 +187,8 @@
 }
 
 // Retry the disconnected local port for 60 times, and sleep 1 second between two retries.
-constexpr uint32_t LOCAL_PORT_RETRY_COUNT = 60;
-constexpr auto LOCAL_PORT_RETRY_INTERVAL = 1s;
+static constexpr uint32_t LOCAL_PORT_RETRY_COUNT = 60;
+static constexpr auto LOCAL_PORT_RETRY_INTERVAL = 1s;
 
 struct RetryPort {
     int port;
@@ -267,7 +270,7 @@
             disable_tcp_nagle(fd.get());
             std::string serial = android::base::StringPrintf("host-%d", fd.get());
             register_socket_transport(std::move(fd), std::move(serial), port, 1,
-                                      [](atransport*) { return false; });
+                                      [](atransport*) { return ReconnectResult::Abort; });
         }
     }
     D("transport: server_socket_thread() exiting");
@@ -364,7 +367,7 @@
                 std::string serial = android::base::StringPrintf("host-%d", fd.get());
                 WriteFdExactly(fd.get(), _start_req, strlen(_start_req));
                 register_socket_transport(std::move(fd), std::move(serial), port, 1,
-                                          [](atransport*) { return false; });
+                                          [](atransport*) { return ReconnectResult::Abort; });
             }
 
             /* Prepare for accepting of the next ADB host connection. */
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index 49cc0c9..1748665 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -170,7 +170,9 @@
 //
 //  In either case this macro has no effect on runtime behavior and performance
 //  of code.
+#ifndef FALLTHROUGH_INTENDED
 #define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
+#endif
 
 // Current ABI string
 #if defined(__arm__)
diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h
index 55f1ed3..9444fdd 100644
--- a/base/include/android-base/parseint.h
+++ b/base/include/android-base/parseint.h
@@ -33,18 +33,35 @@
 template <typename T>
 bool ParseUint(const char* s, T* out, T max = std::numeric_limits<T>::max(),
                bool allow_suffixes = false) {
+  while (isspace(*s)) {
+    s++;
+  }
+
+  if (s[0] == '-') {
+    errno = EINVAL;
+    return false;
+  }
+
   int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
   errno = 0;
   char* end;
   unsigned long long int result = strtoull(s, &end, base);
-  if (errno != 0 || end == s) return false;
+  if (errno != 0) return false;
+  if (end == s) {
+    errno = EINVAL;
+    return false;
+  }
   if (*end != '\0') {
     const char* suffixes = "bkmgtpe";
     const char* suffix;
-    if (!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) return false;
-    if (__builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) return false;
+    if ((!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) ||
+        __builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) {
+      errno = EINVAL;
+      return false;
+    }
   }
   if (max < result) {
+    errno = ERANGE;
     return false;
   }
   if (out != nullptr) {
@@ -79,14 +96,23 @@
 bool ParseInt(const char* s, T* out,
               T min = std::numeric_limits<T>::min(),
               T max = std::numeric_limits<T>::max()) {
+  while (isspace(*s)) {
+    s++;
+  }
+
   int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10;
   errno = 0;
   char* end;
   long long int result = strtoll(s, &end, base);
-  if (errno != 0 || s == end || *end != '\0') {
+  if (errno != 0) {
+    return false;
+  }
+  if (s == end || *end != '\0') {
+    errno = EINVAL;
     return false;
   }
   if (result < min || max < result) {
+    errno = ERANGE;
     return false;
   }
   if (out != nullptr) {
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 9e2ea97..2abe68e 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -62,16 +62,18 @@
   CapturedStdFd(int std_fd);
   ~CapturedStdFd();
 
-  int fd() const;
   std::string str();
 
- private:
-  void Init();
+  void Start();
+  void Stop();
   void Reset();
 
+ private:
+  int fd() const;
+
   TemporaryFile temp_file_;
   int std_fd_;
-  int old_fd_;
+  int old_fd_ = -1;
 
   DISALLOW_COPY_AND_ASSIGN(CapturedStdFd);
 };
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index c6936f1..71025ad 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -22,6 +22,7 @@
 #include <sys/socket.h>
 #endif
 
+#include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -199,6 +200,17 @@
   return Socketpair(AF_UNIX, type, 0, left, right);
 }
 
+// Using fdopen with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline FILE* Fdopen(unique_fd&& ufd, const char* mode) {
+  int fd = ufd.release();
+  FILE* file = fdopen(fd, mode);
+  if (!file) {
+    close(fd);
+  }
+  return file;
+}
+
 #endif  // !defined(_WIN32)
 
 }  // namespace base
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 75b4ea0..3113fb4 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -206,10 +206,8 @@
 }
 #endif
 
-static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
+static void CheckMessage(const std::string& output, android::base::LogSeverity severity,
                          const char* expected, const char* expected_tag = nullptr) {
-  std::string output = cap.str();
-
   // We can't usefully check the output of any of these on Windows because we
   // don't have std::regex, but we can at least make sure we printed at least as
   // many characters are in the log message.
@@ -231,20 +229,28 @@
 #endif
 }
 
+static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severity,
+                         const char* expected, const char* expected_tag = nullptr) {
+  cap.Stop();
+  std::string output = cap.str();
+  return CheckMessage(output, severity, expected, expected_tag);
+}
 
-#define CHECK_LOG_STREAM_DISABLED(severity) \
-  { \
+#define CHECK_LOG_STREAM_DISABLED(severity)                      \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    LOG_STREAM(severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
-  { \
+    CapturedStderr cap1;                                         \
+    LOG_STREAM(severity) << "foo bar";                           \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }                                                              \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    LOG_STREAM(::android::base::severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
+    CapturedStderr cap1;                                         \
+    LOG_STREAM(::android::base::severity) << "foo bar";          \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }
 
 #define CHECK_LOG_STREAM_ENABLED(severity) \
   { \
@@ -265,7 +271,7 @@
 }
 
 TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) {
-  CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT));
 }
 
 TEST(logging, LOG_STREAM_ERROR_disabled) {
@@ -273,7 +279,7 @@
 }
 
 TEST(logging, LOG_STREAM_ERROR_enabled) {
-  CHECK_LOG_STREAM_ENABLED(ERROR);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(ERROR));
 }
 
 TEST(logging, LOG_STREAM_WARNING_disabled) {
@@ -281,7 +287,7 @@
 }
 
 TEST(logging, LOG_STREAM_WARNING_enabled) {
-  CHECK_LOG_STREAM_ENABLED(WARNING);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(WARNING));
 }
 
 TEST(logging, LOG_STREAM_INFO_disabled) {
@@ -289,7 +295,7 @@
 }
 
 TEST(logging, LOG_STREAM_INFO_enabled) {
-  CHECK_LOG_STREAM_ENABLED(INFO);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(INFO));
 }
 
 TEST(logging, LOG_STREAM_DEBUG_disabled) {
@@ -297,7 +303,7 @@
 }
 
 TEST(logging, LOG_STREAM_DEBUG_enabled) {
-  CHECK_LOG_STREAM_ENABLED(DEBUG);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(DEBUG));
 }
 
 TEST(logging, LOG_STREAM_VERBOSE_disabled) {
@@ -305,26 +311,27 @@
 }
 
 TEST(logging, LOG_STREAM_VERBOSE_enabled) {
-  CHECK_LOG_STREAM_ENABLED(VERBOSE);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(VERBOSE));
 }
 
 #undef CHECK_LOG_STREAM_DISABLED
 #undef CHECK_LOG_STREAM_ENABLED
 
-
-#define CHECK_LOG_DISABLED(severity) \
-  { \
+#define CHECK_LOG_DISABLED(severity)                             \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    LOG(severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
-  { \
+    CapturedStderr cap1;                                         \
+    LOG(severity) << "foo bar";                                  \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }                                                              \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    LOG(::android::base::severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
+    CapturedStderr cap1;                                         \
+    LOG(::android::base::severity) << "foo bar";                 \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }
 
 #define CHECK_LOG_ENABLED(severity) \
   { \
@@ -350,7 +357,7 @@
 }
 
 TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) {
-  CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT));
 }
 
 TEST(logging, LOG_ERROR_disabled) {
@@ -358,7 +365,7 @@
 }
 
 TEST(logging, LOG_ERROR_enabled) {
-  CHECK_LOG_ENABLED(ERROR);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(ERROR));
 }
 
 TEST(logging, LOG_WARNING_disabled) {
@@ -366,7 +373,7 @@
 }
 
 TEST(logging, LOG_WARNING_enabled) {
-  CHECK_LOG_ENABLED(WARNING);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(WARNING));
 }
 
 TEST(logging, LOG_INFO_disabled) {
@@ -374,7 +381,7 @@
 }
 
 TEST(logging, LOG_INFO_enabled) {
-  CHECK_LOG_ENABLED(INFO);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(INFO));
 }
 
 TEST(logging, LOG_DEBUG_disabled) {
@@ -382,7 +389,7 @@
 }
 
 TEST(logging, LOG_DEBUG_enabled) {
-  CHECK_LOG_ENABLED(DEBUG);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(DEBUG));
 }
 
 TEST(logging, LOG_VERBOSE_disabled) {
@@ -390,28 +397,28 @@
 }
 
 TEST(logging, LOG_VERBOSE_enabled) {
-  CHECK_LOG_ENABLED(VERBOSE);
+  ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(VERBOSE));
 }
 
 #undef CHECK_LOG_DISABLED
 #undef CHECK_LOG_ENABLED
 
-
 TEST(logging, LOG_complex_param) {
-#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info)             \
-  {                                                                                                \
-    android::base::ScopedLogSeverity sls(                                                          \
-        (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING);        \
-    CapturedStderr cap;                                                                            \
-    LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING)            \
-        << "foobar";                                                                               \
-    if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) {                          \
-      CheckMessage(cap,                                                                            \
-                   (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
-                   "foobar");                                                                      \
-    } else {                                                                                       \
-      ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_CUR));                                                  \
-    }                                                                                              \
+#define CHECK_LOG_COMBINATION(use_scoped_log_severity_info, use_logging_severity_info)         \
+  {                                                                                            \
+    android::base::ScopedLogSeverity sls(                                                      \
+        (use_scoped_log_severity_info) ? ::android::base::INFO : ::android::base::WARNING);    \
+    CapturedStderr cap;                                                                        \
+    LOG((use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING)        \
+        << "foobar";                                                                           \
+    if ((use_scoped_log_severity_info) || !(use_logging_severity_info)) {                      \
+      ASSERT_NO_FATAL_FAILURE(CheckMessage(                                                    \
+          cap, (use_logging_severity_info) ? ::android::base::INFO : ::android::base::WARNING, \
+          "foobar"));                                                                          \
+    } else {                                                                                   \
+      cap.Stop();                                                                              \
+      ASSERT_EQ("", cap.str());                                                                \
+    }                                                                                          \
   }
 
   CHECK_LOG_COMBINATION(false,false);
@@ -429,7 +436,7 @@
   LOG(INFO) << (errno = 67890);
   EXPECT_EQ(12345, errno) << "errno was not restored";
 
-  CheckMessage(cap, android::base::INFO, "67890");
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
 }
 
 TEST(logging, PLOG_does_not_clobber_errno) {
@@ -438,7 +445,7 @@
   PLOG(INFO) << (errno = 67890);
   EXPECT_EQ(12345, errno) << "errno was not restored";
 
-  CheckMessage(cap, android::base::INFO, "67890");
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::INFO, "67890"));
 }
 
 TEST(logging, LOG_does_not_have_dangling_if) {
@@ -464,19 +471,21 @@
   EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
 }
 
-#define CHECK_PLOG_DISABLED(severity) \
-  { \
+#define CHECK_PLOG_DISABLED(severity)                            \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    PLOG(severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
-  { \
+    CapturedStderr cap1;                                         \
+    PLOG(severity) << "foo bar";                                 \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }                                                              \
+  {                                                              \
     android::base::ScopedLogSeverity sls1(android::base::FATAL); \
-    CapturedStderr cap1; \
-    PLOG(severity) << "foo bar"; \
-    ASSERT_EQ(0, lseek(cap1.fd(), 0, SEEK_CUR)); \
-  } \
+    CapturedStderr cap1;                                         \
+    PLOG(severity) << "foo bar";                                 \
+    cap1.Stop();                                                 \
+    ASSERT_EQ("", cap1.str());                                   \
+  }
 
 #define CHECK_PLOG_ENABLED(severity) \
   { \
@@ -504,7 +513,7 @@
 }
 
 TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) {
-  CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT));
 }
 
 TEST(logging, PLOG_ERROR_disabled) {
@@ -512,7 +521,7 @@
 }
 
 TEST(logging, PLOG_ERROR_enabled) {
-  CHECK_PLOG_ENABLED(ERROR);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(ERROR));
 }
 
 TEST(logging, PLOG_WARNING_disabled) {
@@ -520,7 +529,7 @@
 }
 
 TEST(logging, PLOG_WARNING_enabled) {
-  CHECK_PLOG_ENABLED(WARNING);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(WARNING));
 }
 
 TEST(logging, PLOG_INFO_disabled) {
@@ -528,7 +537,7 @@
 }
 
 TEST(logging, PLOG_INFO_enabled) {
-  CHECK_PLOG_ENABLED(INFO);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(INFO));
 }
 
 TEST(logging, PLOG_DEBUG_disabled) {
@@ -536,7 +545,7 @@
 }
 
 TEST(logging, PLOG_DEBUG_enabled) {
-  CHECK_PLOG_ENABLED(DEBUG);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(DEBUG));
 }
 
 TEST(logging, PLOG_VERBOSE_disabled) {
@@ -544,7 +553,7 @@
 }
 
 TEST(logging, PLOG_VERBOSE_enabled) {
-  CHECK_PLOG_ENABLED(VERBOSE);
+  ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(VERBOSE));
 }
 
 #undef CHECK_PLOG_DISABLED
@@ -557,7 +566,7 @@
   CapturedStderr cap;
   errno = ENOENT;
   UNIMPLEMENTED(ERROR);
-  CheckMessage(cap, android::base::ERROR, expected.c_str());
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(cap, android::base::ERROR, expected.c_str()));
 }
 
 static void NoopAborter(const char* msg ATTRIBUTE_UNUSED) {
@@ -565,17 +574,19 @@
 }
 
 TEST(logging, LOG_FATAL_NOOP_ABORTER) {
+  CapturedStderr cap;
   {
     android::base::SetAborter(NoopAborter);
 
     android::base::ScopedLogSeverity sls(android::base::ERROR);
-    CapturedStderr cap;
     LOG(FATAL) << "foobar";
-    CheckMessage(cap, android::base::FATAL, "foobar");
-    CheckMessage(cap, android::base::ERROR, "called noop");
+    cap.Stop();
 
     android::base::SetAborter(android::base::DefaultAborter);
   }
+  std::string output = cap.str();
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::FATAL, "foobar"));
+  ASSERT_NO_FATAL_FAILURE(CheckMessage(output, android::base::ERROR, "called noop"));
 
   ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
 }
@@ -619,25 +630,21 @@
     LOG(INFO) << expected_msg;
     android::base::SetDefaultTag(old_default_tag);
   }
-  CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag);
+  ASSERT_NO_FATAL_FAILURE(
+      CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag));
 }
 
 TEST(logging, StdioLogger) {
-  std::string err_str;
-  std::string out_str;
-  {
-    CapturedStderr cap_err;
-    CapturedStdout cap_out;
-    android::base::SetLogger(android::base::StdioLogger);
-    LOG(INFO) << "out";
-    LOG(ERROR) << "err";
-    err_str = cap_err.str();
-    out_str = cap_out.str();
-  }
+  CapturedStderr cap_err;
+  CapturedStdout cap_out;
+  android::base::SetLogger(android::base::StdioLogger);
+  LOG(INFO) << "out";
+  LOG(ERROR) << "err";
+  cap_err.Stop();
+  cap_out.Stop();
 
   // For INFO we expect just the literal "out\n".
-  ASSERT_EQ("out\n", out_str) << out_str;
+  ASSERT_EQ("out\n", cap_out.str());
   // Whereas ERROR logging includes the program name.
-  ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", err_str)
-      << err_str;
+  ASSERT_EQ(android::base::Basename(android::base::GetExecutablePath()) + ": err\n", cap_err.str());
 }
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index 8f9ed77..e449c33 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -16,17 +16,30 @@
 
 #include "android-base/parseint.h"
 
+#include <errno.h>
+
 #include <gtest/gtest.h>
 
 TEST(parseint, signed_smoke) {
+  errno = 0;
   int i = 0;
   ASSERT_FALSE(android::base::ParseInt("x", &i));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseInt("123x", &i));
+  ASSERT_EQ(EINVAL, errno);
 
   ASSERT_TRUE(android::base::ParseInt("123", &i));
   ASSERT_EQ(123, i);
+  ASSERT_EQ(0, errno);
+  i = 0;
+  EXPECT_TRUE(android::base::ParseInt("  123", &i));
+  EXPECT_EQ(123, i);
   ASSERT_TRUE(android::base::ParseInt("-123", &i));
   ASSERT_EQ(-123, i);
+  i = 0;
+  EXPECT_TRUE(android::base::ParseInt("  -123", &i));
+  EXPECT_EQ(-123, i);
 
   short s = 0;
   ASSERT_TRUE(android::base::ParseInt("1234", &s));
@@ -34,22 +47,43 @@
 
   ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
   ASSERT_EQ(12, i);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
+  ASSERT_EQ(ERANGE, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
+  ASSERT_EQ(ERANGE, errno);
 
+  errno = 0;
   ASSERT_FALSE(android::base::ParseInt<int>("x", nullptr));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseInt<int>("123x", nullptr));
+  ASSERT_EQ(EINVAL, errno);
   ASSERT_TRUE(android::base::ParseInt<int>("1234", nullptr));
 }
 
 TEST(parseint, unsigned_smoke) {
+  errno = 0;
   unsigned int i = 0u;
   ASSERT_FALSE(android::base::ParseUint("x", &i));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint("123x", &i));
+  ASSERT_EQ(EINVAL, errno);
 
   ASSERT_TRUE(android::base::ParseUint("123", &i));
   ASSERT_EQ(123u, i);
+  ASSERT_EQ(0, errno);
+  i = 0u;
+  EXPECT_TRUE(android::base::ParseUint("  123", &i));
+  EXPECT_EQ(123u, i);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint("-123", &i));
+  EXPECT_EQ(EINVAL, errno);
+  errno = 0;
+  EXPECT_FALSE(android::base::ParseUint("  -123", &i));
+  EXPECT_EQ(EINVAL, errno);
 
   unsigned short s = 0u;
   ASSERT_TRUE(android::base::ParseUint("1234", &s));
@@ -57,12 +91,28 @@
 
   ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
   ASSERT_EQ(12u, i);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
+  ASSERT_EQ(ERANGE, errno);
 
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint<unsigned short>("x", nullptr));
+  ASSERT_EQ(EINVAL, errno);
+  errno = 0;
   ASSERT_FALSE(android::base::ParseUint<unsigned short>("123x", nullptr));
+  ASSERT_EQ(EINVAL, errno);
   ASSERT_TRUE(android::base::ParseUint<unsigned short>("1234", nullptr));
+
+  errno = 0;
+  unsigned long long int lli;
+  EXPECT_FALSE(android::base::ParseUint("-123", &lli));
+  EXPECT_EQ(EINVAL, errno);
+  errno = 0;
+  EXPECT_FALSE(android::base::ParseUint("  -123", &lli));
+  EXPECT_EQ(EINVAL, errno);
 }
 
 TEST(parseint, no_implicit_octal) {
@@ -79,10 +129,16 @@
   int i = 0;
   ASSERT_TRUE(android::base::ParseInt("0x123", &i));
   ASSERT_EQ(0x123, i);
+  i = 0;
+  EXPECT_TRUE(android::base::ParseInt("  0x123", &i));
+  EXPECT_EQ(0x123, i);
 
   unsigned int u = 0u;
   ASSERT_TRUE(android::base::ParseUint("0x123", &u));
   ASSERT_EQ(0x123u, u);
+  u = 0u;
+  EXPECT_TRUE(android::base::ParseUint("  0x123", &u));
+  EXPECT_EQ(0x123u, u);
 }
 
 TEST(parseint, string) {
@@ -101,7 +157,7 @@
   ASSERT_EQ(123, i);
 
   unsigned int u = 123u;
-  ASSERT_FALSE(android::base::ParseInt("456x", &u));
+  ASSERT_FALSE(android::base::ParseUint("456x", &u));
   ASSERT_EQ(123u, u);
 }
 
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 5096369..4d9466b 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -126,11 +126,13 @@
 }
 
 CapturedStdFd::CapturedStdFd(int std_fd) : std_fd_(std_fd), old_fd_(-1) {
-  Init();
+  Start();
 }
 
 CapturedStdFd::~CapturedStdFd() {
-  Reset();
+  if (old_fd_ != -1) {
+    Stop();
+  }
 }
 
 int CapturedStdFd::fd() const {
@@ -144,19 +146,28 @@
   return result;
 }
 
-void CapturedStdFd::Init() {
+void CapturedStdFd::Reset() {
+  // Do not reset while capturing.
+  CHECK_EQ(-1, old_fd_);
+  CHECK_EQ(0, TEMP_FAILURE_RETRY(lseek(fd(), 0, SEEK_SET)));
+  CHECK_EQ(0, ftruncate(fd(), 0));
+}
+
+void CapturedStdFd::Start() {
 #if defined(_WIN32)
   // On Windows, stderr is often buffered, so make sure it is unbuffered so
   // that we can immediately read back what was written to stderr.
-  if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
+  if (std_fd_ == STDERR_FILENO) CHECK_EQ(0, setvbuf(stderr, nullptr, _IONBF, 0));
 #endif
   old_fd_ = dup(std_fd_);
   CHECK_NE(-1, old_fd_);
   CHECK_NE(-1, dup2(fd(), std_fd_));
 }
 
-void CapturedStdFd::Reset() {
+void CapturedStdFd::Stop() {
+  CHECK_NE(-1, old_fd_);
   CHECK_NE(-1, dup2(old_fd_, std_fd_));
-  CHECK_EQ(0, close(old_fd_));
+  close(old_fd_);
+  old_fd_ = -1;
   // Note: cannot restore prior setvbuf() setting.
 }
diff --git a/base/test_utils_test.cpp b/base/test_utils_test.cpp
index 597271a..15a79dd 100644
--- a/base/test_utils_test.cpp
+++ b/base/test_utils_test.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <stdio.h>
+
 #include "android-base/test_utils.h"
 
 #include <gtest/gtest-spi.h>
@@ -42,5 +44,43 @@
   EXPECT_NONFATAL_FAILURE(EXPECT_NOT_MATCH("foobar", R"(foobar)"), "regex mismatch");
 }
 
+TEST(TestUtilsTest, CaptureStdout_smoke) {
+  CapturedStdout cap;
+  printf("This should be captured.\n");
+  cap.Stop();
+  printf("This will not be captured.\n");
+  ASSERT_EQ("This should be captured.\n", cap.str());
+
+  cap.Start();
+  printf("And this text should be captured too.\n");
+  cap.Stop();
+  ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
+
+  printf("Still not going to be captured.\n");
+  cap.Reset();
+  cap.Start();
+  printf("Only this will be captured.\n");
+  ASSERT_EQ("Only this will be captured.\n", cap.str());
+}
+
+TEST(TestUtilsTest, CaptureStderr_smoke) {
+  CapturedStderr cap;
+  fprintf(stderr, "This should be captured.\n");
+  cap.Stop();
+  fprintf(stderr, "This will not be captured.\n");
+  ASSERT_EQ("This should be captured.\n", cap.str());
+
+  cap.Start();
+  fprintf(stderr, "And this text should be captured too.\n");
+  cap.Stop();
+  ASSERT_EQ("This should be captured.\nAnd this text should be captured too.\n", cap.str());
+
+  fprintf(stderr, "Still not going to be captured.\n");
+  cap.Reset();
+  cap.Start();
+  fprintf(stderr, "Only this will be captured.\n");
+  ASSERT_EQ("Only this will be captured.\n", cap.str());
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index cb7cbbe..77f3515 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -143,12 +143,16 @@
   ssize_t rc =
       TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
-    LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+    LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
+               << "timeout reached?";
+    return false;
+  } else if (rc == -1) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned";
     return false;
   } else if (rc != sizeof(response)) {
-    LOG(ERROR)
-        << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
-        << sizeof(response) << ", received " << rc;
+    LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
+                  "reading initial response: expected "
+               << sizeof(response) << ", received " << rc;
     return false;
   }
 
@@ -164,12 +168,16 @@
 
   rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
-    LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+    LOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned: "
+                  "timeout reached?";
+    return false;
+  } else if (rc == -1) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned";
     return false;
   } else if (rc != sizeof(response)) {
-    LOG(ERROR)
-      << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
-      << sizeof(response) << ", received " << rc;
+    LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
+                  "reading confirmation response: expected "
+               << sizeof(response) << ", received " << rc;
     return false;
   }
 
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 93f7572..577e336 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -34,6 +34,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -282,6 +283,7 @@
   switch (crash_info->header.version) {
     case 2:
       *fdsan_table_address = crash_info->data.v2.fdsan_table_address;
+      FALLTHROUGH_INTENDED;
     case 1:
       *abort_msg_address = crash_info->data.v1.abort_msg_address;
       *siginfo = crash_info->data.v1.siginfo;
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f0fe1d0..f0bdfbf 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -224,7 +224,7 @@
     // Prefixes.
     if (!strncmp(arg, "wait-", strlen("wait-"))) {
       char buf[1];
-      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+      UNUSED(TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf))));
       return do_action(arg + strlen("wait-"));
     } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
       errno = 0;
@@ -258,10 +258,14 @@
       __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
       __assert2("some_file.c", 123, "some_function", "false");
+#if !defined(__clang_analyzer__)
     } else if (!strcasecmp(arg, "fortify")) {
+      // FORTIFY is disabled when running clang-tidy and other tools, so this
+      // shouldn't depend on internal implementation details of it.
       char buf[10];
       __read_chk(-1, buf, 32, 10);
       while (true) pause();
+#endif
     } else if (!strcasecmp(arg, "fdsan_file")) {
       FILE* f = fopen("/dev/null", "r");
       close(fileno(f));
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index dfb7a6a..bea8b43 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -29,6 +29,7 @@
 #include <regex>
 #include <thread>
 
+#include <android/fdsan.h>
 #include <android/set_abort_message.h>
 
 #include <android-base/file.h>
@@ -36,6 +37,7 @@
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
@@ -586,9 +588,28 @@
     "/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
 
 static pid_t seccomp_fork_impl(void (*prejail)()) {
-  unique_fd policy_fd(open(kDebuggerdSeccompPolicy, O_RDONLY | O_CLOEXEC));
-  if (policy_fd == -1) {
-    LOG(FATAL) << "failed to open policy " << kDebuggerdSeccompPolicy;
+  std::string policy;
+  if (!android::base::ReadFileToString(kDebuggerdSeccompPolicy, &policy)) {
+    PLOG(FATAL) << "failed to read policy file";
+  }
+
+  // Allow a bunch of syscalls used by the tests.
+  policy += "\nclone: 1";
+  policy += "\nsigaltstack: 1";
+  policy += "\nnanosleep: 1";
+
+  FILE* tmp_file = tmpfile();
+  if (!tmp_file) {
+    PLOG(FATAL) << "tmpfile failed";
+  }
+
+  unique_fd tmp_fd(dup(fileno(tmp_file)));
+  if (!android::base::WriteStringToFd(policy, tmp_fd.get())) {
+    PLOG(FATAL) << "failed to write policy to tmpfile";
+  }
+
+  if (lseek(tmp_fd.get(), 0, SEEK_SET) != 0) {
+    PLOG(FATAL) << "failed to seek tmp_fd";
   }
 
   ScopedMinijail jail{minijail_new()};
@@ -599,7 +620,7 @@
   minijail_no_new_privs(jail.get());
   minijail_log_seccomp_filter_failures(jail.get());
   minijail_use_seccomp_filter(jail.get());
-  minijail_parse_seccomp_filters_from_fd(jail.get(), policy_fd.release());
+  minijail_parse_seccomp_filters_from_fd(jail.get(), tmp_fd.release());
 
   pid_t result = fork();
   if (result == -1) {
@@ -734,6 +755,16 @@
   ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
 }
 
+extern "C" void foo() {
+  LOG(INFO) << "foo";
+  std::this_thread::sleep_for(1s);
+}
+
+extern "C" void bar() {
+  LOG(INFO) << "bar";
+  std::this_thread::sleep_for(1s);
+}
+
 TEST_F(CrasherTest, seccomp_backtrace) {
   int intercept_result;
   unique_fd output_fd;
@@ -741,6 +772,11 @@
   static const auto dump_type = kDebuggerdNativeBacktrace;
   StartProcess(
       []() {
+        std::thread a(foo);
+        std::thread b(bar);
+
+        std::this_thread::sleep_for(100ms);
+
         raise_debugger_signal(dump_type);
         _exit(0);
       },
@@ -755,6 +791,8 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+  ASSERT_BACKTRACE_FRAME(result, "foo");
+  ASSERT_BACKTRACE_FRAME(result, "bar");
 }
 
 TEST_F(CrasherTest, seccomp_crash_logcat) {
@@ -801,6 +839,31 @@
   AssertDeath(SIGABRT);
 }
 
+TEST_F(CrasherTest, fdsan_warning_abort_message) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  StartProcess([]() {
+    android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
+    unique_fd fd(open("/dev/null", O_RDONLY | O_CLOEXEC));
+    if (fd == -1) {
+      abort();
+    }
+    close(fd.get());
+    _exit(0);
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(0);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, "Abort message: 'attempted to close");
+}
+
 TEST(crash_dump, zombie) {
   pid_t forkpid = fork();
 
@@ -991,3 +1054,42 @@
   ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
   ASSERT_STREQ("any", outbuf);
 }
+
+TEST(tombstoned, interceptless_backtrace) {
+  // Generate 50 backtraces, and then check to see that we haven't created 50 new tombstones.
+  auto get_tombstone_timestamps = []() -> std::map<int, time_t> {
+    std::map<int, time_t> result;
+    for (int i = 0; i < 99; ++i) {
+      std::string path = android::base::StringPrintf("/data/tombstones/tombstone_%02d", i);
+      struct stat st;
+      if (stat(path.c_str(), &st) == 0) {
+        result[i] = st.st_mtim.tv_sec;
+      }
+    }
+    return result;
+  };
+
+  auto before = get_tombstone_timestamps();
+  for (int i = 0; i < 50; ++i) {
+    raise_debugger_signal(kDebuggerdNativeBacktrace);
+  }
+  auto after = get_tombstone_timestamps();
+
+  int diff = 0;
+  for (int i = 0; i < 99; ++i) {
+    if (after.count(i) == 0) {
+      continue;
+    }
+    if (before.count(i) == 0) {
+      ++diff;
+      continue;
+    }
+    if (before[i] != after[i]) {
+      ++diff;
+    }
+  }
+
+  // We can't be sure that nothing's crash looping in the background.
+  // This should be good enough, though...
+  ASSERT_LT(diff, 10) << "too many new tombstones; is something crashing in the background?";
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 079a574..ed7423b 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -47,6 +47,7 @@
 #include <unwindstack/Regs.h>
 
 #include "debuggerd/handler.h"
+#include "handler/fallback.h"
 #include "tombstoned/tombstoned.h"
 #include "util.h"
 
@@ -187,7 +188,7 @@
 static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
   static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));
 
-  if (info->si_value.sival_int == ~0) {
+  if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) {
     // Asked to dump by the original signal recipient.
     uint64_t val = trace_output.load();
     auto [tid, fd] = unpack_thread_fd(val);
@@ -259,7 +260,7 @@
 
         siginfo_t siginfo = {};
         siginfo.si_code = SI_QUEUE;
-        siginfo.si_value.sival_int = ~0;
+        siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump;
         siginfo.si_pid = getpid();
         siginfo.si_uid = getuid();
 
@@ -331,7 +332,7 @@
 
 extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
                                            void* abort_message) {
-  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) {
+  if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_ptr != nullptr) {
     return trace_handler(info, ucontext);
   } else {
     return crash_handler(info, ucontext, abort_message);
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 91e6f71..a064ca0 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -58,6 +58,8 @@
 #include "dump_type.h"
 #include "protocol.h"
 
+#include "handler/fallback.h"
+
 using android::base::Pipe;
 
 // We muck with our fds in a 'thread' that doesn't share the same fd table.
@@ -457,14 +459,14 @@
     info = nullptr;
   }
 
-  struct siginfo si = {};
+  struct siginfo dummy_info = {};
   if (!info) {
-    memset(&si, 0, sizeof(si));
-    si.si_signo = signal_number;
-    si.si_code = SI_USER;
-    si.si_pid = __getpid();
-    si.si_uid = getuid();
-    info = &si;
+    memset(&dummy_info, 0, sizeof(dummy_info));
+    dummy_info.si_signo = signal_number;
+    dummy_info.si_code = SI_USER;
+    dummy_info.si_pid = __getpid();
+    dummy_info.si_uid = getuid();
+    info = &dummy_info;
   } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
     // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
     // that contain commit 66dd34a (3.9+). The manpage claims to only allow
@@ -473,8 +475,20 @@
   }
 
   void* abort_message = nullptr;
-  if (signal_number != DEBUGGER_SIGNAL && g_callbacks.get_abort_message) {
-    abort_message = g_callbacks.get_abort_message();
+  uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
+  if (signal_number == DEBUGGER_SIGNAL) {
+    if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
+      // Allow for the abort message to be explicitly specified via the sigqueue value.
+      // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
+      if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) {
+        abort_message = reinterpret_cast<void*>(si_val & ~1);
+        info->si_ptr = reinterpret_cast<void*>(si_val & 1);
+      }
+    }
+  } else {
+    if (g_callbacks.get_abort_message) {
+      abort_message = g_callbacks.get_abort_message();
+    }
   }
 
   // If sival_int is ~0, it means that the fallback handler has been called
@@ -482,7 +496,8 @@
   // of a specific thread. It is possible that the prctl call might return 1,
   // then return 0 in subsequent calls, so check the sival_int to determine if
   // the fallback handler should be called first.
-  if (info->si_value.sival_int == ~0 || prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+  if (si_val == kDebuggerdFallbackSivalUintptrRequestDump ||
+      prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
     // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
     // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
     // ANR trace.
diff --git a/libmemunreachable/anon_vma_naming.h b/debuggerd/handler/fallback.h
similarity index 60%
copy from libmemunreachable/anon_vma_naming.h
copy to debuggerd/handler/fallback.h
index fb31e41..597f582 100644
--- a/libmemunreachable/anon_vma_naming.h
+++ b/debuggerd/handler/fallback.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-#ifndef LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
-#define LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#pragma once
 
-#include <sys/prctl.h>
+#include <stdint.h>
 
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
-
-#endif  // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+static void* const kDebuggerdFallbackSivalPtrRequestDump = reinterpret_cast<void*>(~0UL);
+static const uintptr_t kDebuggerdFallbackSivalUintptrRequestDump = ~0UL;
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index 1fdf236..743a2e7 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -18,6 +18,7 @@
 
 #include "libdebuggerd/open_files_list.h"
 
+#include <android/fdsan.h>
 #include <dirent.h>
 #include <errno.h>
 #include <stdio.h>
@@ -75,7 +76,6 @@
       ALOGE("failed to read fdsan table entry %zu: %s", i, strerror(errno));
       return;
     }
-    ALOGE("fd %zu = %#" PRIx64, i, entry.close_tag.load());
     if (entry.close_tag) {
       (*list)[i].fdsan_owner = entry.close_tag.load();
     }
@@ -123,8 +123,10 @@
     const std::optional<std::string>& path = entry.path;
     const std::optional<uint64_t>& fdsan_owner = entry.fdsan_owner;
     if (path && fdsan_owner) {
-      _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (owned by %#" PRIx64 ")\n", prefix, fd,
-           path->c_str(), *fdsan_owner);
+      const char* type = android_fdsan_get_tag_type(*fdsan_owner);
+      uint64_t value = android_fdsan_get_tag_value(*fdsan_owner);
+      _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (owned by %s %#" PRIx64 ")\n", prefix, fd,
+           path->c_str(), type, value);
     } else if (path && !fdsan_owner) {
       _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (unowned)\n", prefix, fd, path->c_str());
     } else if (!path && fdsan_owner) {
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 15ae406..ad92067 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -212,8 +212,13 @@
   bool intercepted =
       intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd);
   if (!intercepted) {
-    std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
-    crash->crash_tombstone_fd.reset(dup(output_fd.get()));
+    if (crash->crash_type == kDebuggerdNativeBacktrace) {
+      // Don't generate tombstones for native backtrace requests.
+      output_fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
+    } else {
+      std::tie(crash->crash_tombstone_path, output_fd) = CrashQueue::for_crash(crash)->get_output();
+      crash->crash_tombstone_fd.reset(dup(output_fd.get()));
+    }
   }
 
   TombstonedCrashPacket response = {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 3414d53..6b175af 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// This is required because no Android.bp can include a library defined in an
+// Android.mk. Eventually should kill libfastboot (defined in Android.mk)
 cc_library_host_static {
     name: "libfastboot2",
 
@@ -19,50 +21,66 @@
 
     compile_multilib: "first",
     srcs: [
-      "bootimg_utils.cpp",
-      "fs.cpp",
-      "socket.cpp",
-      "tcp.cpp",
-      "udp.cpp",
-      "util.cpp",
-      "fastboot_driver.cpp",
+        "bootimg_utils.cpp",
+        "fs.cpp",
+        "socket.cpp",
+        "tcp.cpp",
+        "udp.cpp",
+        "util.cpp",
+        "fastboot_driver.cpp",
     ],
 
     static_libs: [
-      "libziparchive",
-      "libsparse",
-      "libutils",
-      "liblog",
-      "libz",
-      "libdiagnose_usb",
-      "libbase",
-      "libcutils",
-      "libgtest",
-      "libgtest_main",
-      "libbase",
-      "libadb_host"
+        "libziparchive",
+        "libsparse",
+        "libutils",
+        "liblog",
+        "libz",
+        "libdiagnose_usb",
+        "libbase",
+        "libcutils",
+        "libgtest",
+        "libgtest_main",
+        "libbase",
+        "libadb_host",
     ],
 
     header_libs: [
-      "bootimg_headers"
+        "bootimg_headers",
     ],
 
     export_header_lib_headers: [
-      "bootimg_headers"
+        "bootimg_headers",
     ],
 
-
     target: {
-      linux: {
-        srcs: ["usb_linux.cpp"],
-      },
+        linux: {
+            srcs: ["usb_linux.cpp"],
+        },
+
+        darwin: {
+            srcs: ["usb_osx.cpp"],
+
+            host_ldlibs: [
+                "-framework CoreFoundation",
+                "-framework IOKit",
+            ],
+        },
+
+        windows: {
+            srcs: ["usb_windows.cpp"],
+
+            host_ldlibs: [
+                "-lws2_32",
+            ],
+        },
     },
 
     cflags: [
-      "-Wall",
-      "-Wextra",
-      "-Werror",
-      "-Wunreachable-code",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wunreachable-code",
     ],
 
     export_include_dirs: ["."],
@@ -103,6 +121,7 @@
 
     shared_libs: [
         "android.hardware.boot@1.0",
+        "android.hardware.fastboot@1.0",
         "libadbd",
         "libasyncio",
         "libbase",
@@ -122,3 +141,154 @@
 
     cpp_std: "c++17",
 }
+
+cc_defaults {
+    name: "fastboot_host_defaults",
+
+    use_version_lib: true,
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wunreachable-code",
+    ],
+
+    target: {
+        darwin: {
+            cflags: ["-Wno-unused-parameter"],
+            host_ldlibs: [
+                "-lpthread",
+                "-framework CoreFoundation",
+                "-framework IOKit",
+            ],
+        },
+        windows: {
+            enabled: true,
+
+            host_ldlibs: ["-lws2_32"],
+        },
+    },
+
+    stl: "libc++_static",
+
+    // Don't add anything here, we don't want additional shared dependencies
+    // on the host fastboot tool, and shared libraries that link against libc++
+    // will violate ODR.
+    shared_libs: [],
+
+    header_libs: ["bootimg_headers"],
+    static_libs: [
+        "libziparchive",
+        "libsparse",
+        "libutils",
+        "liblog",
+        "libz",
+        "libdiagnose_usb",
+        "libbase",
+        "libcutils",
+        "libgtest_host",
+    ],
+}
+
+//
+// Build host libfastboot.
+//
+
+cc_library_host_static {
+    name: "libfastboot",
+    defaults: ["fastboot_host_defaults"],
+
+    cpp_std: "c++17",
+    srcs: [
+        "bootimg_utils.cpp",
+        "engine.cpp",
+        "fastboot.cpp",
+        "fs.cpp",
+        "socket.cpp",
+        "tcp.cpp",
+        "udp.cpp",
+        "util.cpp",
+        "fastboot_driver.cpp",
+    ],
+
+    // Only version the final binaries
+    use_version_lib: false,
+    static_libs: ["libbuildversion"],
+
+    generated_headers: ["platform_tools_version"],
+
+    target: {
+        windows: {
+            srcs: ["usb_windows.cpp"],
+
+            include_dirs: ["development/host/windows/usb/api"],
+        },
+        darwin: {
+            srcs: ["usb_osx.cpp"],
+        },
+        linux_glibc: {
+            srcs: ["usb_linux.cpp"],
+        },
+    },
+}
+
+//
+// Build host fastboot / fastboot.exe
+//
+
+cc_binary_host {
+    name: "fastboot",
+    defaults: ["fastboot_host_defaults"],
+
+    srcs: ["main.cpp"],
+    static_libs: ["libfastboot"],
+
+    required: [
+        "mke2fs",
+        "make_f2fs",
+    ],
+
+    target: {
+        not_windows: {
+            required: [
+                "e2fsdroid",
+                "mke2fs.conf",
+                "sload_f2fs",
+            ],
+        },
+        windows: {
+            required: ["AdbWinUsbApi"],
+            shared_libs: ["AdbWinApi"],
+        },
+    },
+}
+
+//
+// Build host fastboot_test.
+//
+
+cc_test_host {
+    name: "fastboot_test",
+    defaults: ["fastboot_host_defaults"],
+
+    srcs: [
+        "fastboot_test.cpp",
+        "socket_mock.cpp",
+        "socket_test.cpp",
+        "tcp_test.cpp",
+        "udp_test.cpp",
+    ],
+
+    static_libs: ["libfastboot"],
+
+    target: {
+        windows: {
+            shared_libs: ["AdbWinApi"],
+        },
+        windows_x86_64: {
+            // Avoid trying to build for win64
+            enabled: false,
+        },
+    },
+}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index a679143..e4c1317 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -14,89 +14,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-include $(LOCAL_PATH)/../platform_tools_tool_version.mk
-
-fastboot_cflags := -Wall -Wextra -Werror -Wunreachable-code
-fastboot_cflags += -DFASTBOOT_VERSION="\"$(tool_version)\""
-fastboot_cflags_darwin := -Wno-unused-parameter
-fastboot_ldlibs_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-fastboot_ldlibs_windows := -lws2_32
-# Don't add anything here, we don't want additional shared dependencies
-# on the host fastboot tool, and shared libraries that link against libc++
-# will violate ODR.
-fastboot_shared_libs :=
-fastboot_static_libs := \
-    libziparchive \
-    libsparse \
-    libutils \
-    liblog \
-    libz \
-    libdiagnose_usb \
-    libbase \
-    libcutils \
-    libgtest_host \
-
-fastboot_stl := libc++_static
-
-#
-# Build host libfastboot.
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libfastboot
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_SRC_FILES := \
-    bootimg_utils.cpp \
-    engine.cpp \
-    fastboot.cpp \
-    fs.cpp \
-    socket.cpp \
-    tcp.cpp \
-    udp.cpp \
-    util.cpp \
-    fastboot_driver.cpp \
-
-LOCAL_SRC_FILES_darwin := usb_osx.cpp
-LOCAL_SRC_FILES_linux := usb_linux.cpp
-LOCAL_SRC_FILES_windows := usb_windows.cpp
-
-LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
-LOCAL_CFLAGS := $(fastboot_cflags)
-LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
-LOCAL_CXX_STL := $(fastboot_stl)
-LOCAL_HEADER_LIBRARIES := bootimg_headers
-LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
-LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
-LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
-LOCAL_STATIC_LIBRARIES := $(fastboot_static_libs)
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-#
-# Build host fastboot / fastboot.exe
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := fastboot
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_CFLAGS := $(fastboot_cflags)
-LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
-LOCAL_CPP_STD := c++17
-LOCAL_CXX_STL := $(fastboot_stl)
-LOCAL_HEADER_LIBRARIES := bootimg_headers
-LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
-LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
-LOCAL_REQUIRED_MODULES := mke2fs make_f2fs
-LOCAL_REQUIRED_MODULES_darwin := e2fsdroid mke2fs.conf sload_f2fs
-LOCAL_REQUIRED_MODULES_linux := e2fsdroid mke2fs.conf sload_f2fs
-LOCAL_REQUIRED_MODULES_windows := AdbWinUsbApi
-LOCAL_SRC_FILES := main.cpp
-LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-LOCAL_STATIC_LIBRARIES := libfastboot $(fastboot_static_libs)
-include $(BUILD_HOST_EXECUTABLE)
-
 #
 # Package fastboot-related executables.
 #
@@ -111,30 +28,3 @@
 $(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_fastboot.BUILT))
 endif
 my_dist_files :=
-
-#
-# Build host fastboot_test.
-#
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := fastboot_test
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_MODULE_HOST_CROSS_ARCH := x86 # Avoid trying to build for win64.
-
-LOCAL_SRC_FILES := \
-    fastboot_test.cpp \
-    socket_mock.cpp \
-    socket_test.cpp \
-    tcp_test.cpp \
-    udp_test.cpp \
-
-LOCAL_CFLAGS := $(fastboot_cflags)
-LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
-LOCAL_CXX_STL := $(fastboot_stl)
-LOCAL_HEADER_LIBRARIES := bootimg_headers
-LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
-LOCAL_LDLIBS_windows := $(fastboot_ldlibs_windows)
-LOCAL_SHARED_LIBRARIES := $(fastboot_shared_libs)
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-LOCAL_STATIC_LIBRARIES := libfastboot $(fastboot_static_libs)
-include $(BUILD_HOST_NATIVE_TEST)
diff --git a/fastboot/README.md b/fastboot/README.md
index ec7dcb4..c224448 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -136,10 +136,6 @@
                        should not support "upload" unless it supports an
                        oem command that requires "upload" capabilities.
 
-    verify:%08x        Send a digital signature to verify the downloaded
-                       data.  Required if the bootloader is "secure"
-                       otherwise "flash" and "boot" will be ignored.
-
     flash:%s           Write the previously downloaded image to the
                        named partition (if possible).
 
@@ -159,8 +155,6 @@
                        the bootloader and then upgrading other partitions
                        using the new bootloader.
 
-    powerdown          Power off the device.
-
 
 
 ## Client Variables
@@ -186,10 +180,45 @@
                         bootloader requiring a signature before
                         it will install or boot images.
 
+    is-userspace        If the value is "yes", the device is running
+                        fastbootd. Otherwise, it is running fastboot
+                        in the bootloader.
+
 Names starting with a lowercase character are reserved by this
 specification.  OEM-specific names should not start with lowercase
 characters.
 
+## Logical Partitions
+
+There are a number of commands to interact with logical partitions:
+
+    update-super:%s:%s  Write the previously downloaded image to a super
+                        partition. Unlike the "flash" command, this has
+                        special rules. The image must have been created by
+                        the lpmake command, and must not be a sparse image.
+                        If the last argument is "wipe", then all existing
+                        logical partitions are deleted. If no final argument
+                        is specified, the partition tables are merged. Any
+                        partition in the new image that does not exist in the
+                        old image is created with a zero size.
+
+                        In all cases, this will cause the temporary "scratch"
+                        partition to be deleted if it exists.
+
+    create-logical-partition:%s:%d
+                        Create a logical partition with the given name and
+                        size, in the super partition.
+
+    delete-logical-partition:%s
+                        Delete a logical partition with the given name.
+
+    resize-logical-partition:%s:%d
+                        Change the size of the named logical partition.
+
+In addition, there is a variable to test whether a partition is logical:
+
+    is-logical:%s       If the value is "yes", the partition is logical.
+                        Otherwise the partition is physical.
 
 ## TCP Protocol v1
 
diff --git a/fastboot/bootimg_utils.cpp b/fastboot/bootimg_utils.cpp
index 1152007..e433787 100644
--- a/fastboot/bootimg_utils.cpp
+++ b/fastboot/bootimg_utils.cpp
@@ -39,27 +39,27 @@
     strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());
 }
 
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
-                           void* second, int64_t second_size, size_t base,
-                           const boot_img_hdr_v1& src, int64_t* bootimg_size) {
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+                           std::vector<char>* out) {
     const size_t page_mask = src.page_size - 1;
 
     int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);
-    int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
-    int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
-    int64_t second_actual = (second_size + page_mask) & (~page_mask);
+    int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);
+    int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);
+    int64_t second_actual = (second.size() + page_mask) & (~page_mask);
 
-    *bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    int64_t bootimg_size = header_actual + kernel_actual + ramdisk_actual + second_actual;
+    out->resize(bootimg_size);
 
-    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(calloc(*bootimg_size, 1));
-    if (hdr == nullptr) die("couldn't allocate boot image: %" PRId64 " bytes", *bootimg_size);
+    boot_img_hdr_v1* hdr = reinterpret_cast<boot_img_hdr_v1*>(out->data());
 
     *hdr = src;
     memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
 
-    hdr->kernel_size =  kernel_size;
-    hdr->ramdisk_size = ramdisk_size;
-    hdr->second_size =  second_size;
+    hdr->kernel_size = kernel.size();
+    hdr->ramdisk_size = ramdisk.size();
+    hdr->second_size = second.size();
 
     hdr->kernel_addr += base;
     hdr->ramdisk_addr += base;
@@ -70,8 +70,9 @@
         hdr->header_size = sizeof(boot_img_hdr_v1);
     }
 
-    memcpy(hdr->magic + hdr->page_size, kernel, kernel_size);
-    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk, ramdisk_size);
-    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second, second_size);
+    memcpy(hdr->magic + hdr->page_size, kernel.data(), kernel.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk.data(), ramdisk.size());
+    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second.data(),
+           second.size());
     return hdr;
 }
diff --git a/fastboot/bootimg_utils.h b/fastboot/bootimg_utils.h
index fe805b0..a4e8870 100644
--- a/fastboot/bootimg_utils.h
+++ b/fastboot/bootimg_utils.h
@@ -33,8 +33,9 @@
 #include <sys/types.h>
 
 #include <string>
+#include <vector>
 
-boot_img_hdr_v1* mkbootimg(void* kernel, int64_t kernel_size, void* ramdisk, int64_t ramdisk_size,
-                           void* second, int64_t second_size, size_t base,
-                           const boot_img_hdr_v1& src, int64_t* bootimg_size);
+boot_img_hdr_v1* mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,
+                           const std::vector<char>& second, size_t base, const boot_img_hdr_v1& src,
+                           std::vector<char>* out);
 void bootimg_set_cmdline(boot_img_hdr_v1* h, const std::string& cmdline);
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 43daab0..2a68a2b 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -18,7 +18,6 @@
 #define FB_CMD_GETVAR "getvar"
 #define FB_CMD_DOWNLOAD "download"
 #define FB_CMD_UPLOAD "upload"
-#define FB_CMD_VERIFY "verify"
 #define FB_CMD_FLASH "flash"
 #define FB_CMD_ERASE "erase"
 #define FB_CMD_BOOT "boot"
@@ -29,10 +28,11 @@
 #define FB_CMD_REBOOT_BOOTLOADER "reboot-bootloader"
 #define FB_CMD_REBOOT_RECOVERY "reboot-recovery"
 #define FB_CMD_REBOOT_FASTBOOT "reboot-fastboot"
-#define FB_CMD_POWERDOWN "powerdown"
 #define FB_CMD_CREATE_PARTITION "create-logical-partition"
 #define FB_CMD_DELETE_PARTITION "delete-logical-partition"
 #define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
+#define FB_CMD_UPDATE_SUPER "update-super"
+#define FB_CMD_OEM "oem"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
@@ -54,7 +54,9 @@
 #define FB_VAR_HAS_SLOT "has-slot"
 #define FB_VAR_SLOT_COUNT "slot-count"
 #define FB_VAR_PARTITION_SIZE "partition-size"
+#define FB_VAR_PARTITION_TYPE "partition-type"
 #define FB_VAR_SLOT_SUCCESSFUL "slot-successful"
 #define FB_VAR_SLOT_UNBOOTABLE "slot-unbootable"
 #define FB_VAR_IS_LOGICAL "is-logical"
 #define FB_VAR_IS_USERSPACE "is-userspace"
+#define FB_VAR_HW_REVISION "hw-revision"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 690bfa8..48c935a 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -40,36 +40,86 @@
 using ::android::hardware::boot::V1_0::BoolResult;
 using ::android::hardware::boot::V1_0::CommandResult;
 using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
+
 using namespace android::fs_mgr;
 
+struct VariableHandlers {
+    // Callback to retrieve the value of a single variable.
+    std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;
+    // Callback to retrieve all possible argument combinations, for getvar all.
+    std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
+};
+
+static void GetAllVars(FastbootDevice* device, const std::string& name,
+                       const VariableHandlers& handlers) {
+    if (!handlers.get_all_args) {
+        std::string message;
+        if (!handlers.get(device, std::vector<std::string>(), &message)) {
+            return;
+        }
+        device->WriteInfo(android::base::StringPrintf("%s:%s", name.c_str(), message.c_str()));
+        return;
+    }
+
+    auto all_args = handlers.get_all_args(device);
+    for (const auto& args : all_args) {
+        std::string message;
+        if (!handlers.get(device, args, &message)) {
+            continue;
+        }
+        std::string arg_string = android::base::Join(args, ":");
+        device->WriteInfo(android::base::StringPrintf("%s:%s:%s", name.c_str(), arg_string.c_str(),
+                                                      message.c_str()));
+    }
+}
+
 bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
-    using VariableHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;
-    const std::unordered_map<std::string, VariableHandler> kVariableMap = {
-            {FB_VAR_VERSION, GetVersion},
-            {FB_VAR_VERSION_BOOTLOADER, GetBootloaderVersion},
-            {FB_VAR_VERSION_BASEBAND, GetBasebandVersion},
-            {FB_VAR_PRODUCT, GetProduct},
-            {FB_VAR_SERIALNO, GetSerial},
-            {FB_VAR_SECURE, GetSecure},
-            {FB_VAR_UNLOCKED, GetUnlocked},
-            {FB_VAR_MAX_DOWNLOAD_SIZE, GetMaxDownloadSize},
-            {FB_VAR_CURRENT_SLOT, ::GetCurrentSlot},
-            {FB_VAR_SLOT_COUNT, GetSlotCount},
-            {FB_VAR_HAS_SLOT, GetHasSlot},
-            {FB_VAR_SLOT_SUCCESSFUL, GetSlotSuccessful},
-            {FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable},
-            {FB_VAR_PARTITION_SIZE, GetPartitionSize},
-            {FB_VAR_IS_LOGICAL, GetPartitionIsLogical},
-            {FB_VAR_IS_USERSPACE, GetIsUserspace}};
+    const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
+            {FB_VAR_VERSION, {GetVersion, nullptr}},
+            {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
+            {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+            {FB_VAR_PRODUCT, {GetProduct, nullptr}},
+            {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+            {FB_VAR_SECURE, {GetSecure, nullptr}},
+            {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
+            {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
+            {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
+            {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
+            {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
+            {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
+            {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
+            {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
+            {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
+            {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
+            {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+            {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}}};
+
+    if (args.size() < 2) {
+        return device->WriteFail("Missing argument");
+    }
+
+    // Special case: return all variables that we can.
+    if (args[1] == "all") {
+        for (const auto& [name, handlers] : kVariableMap) {
+            GetAllVars(device, name, handlers);
+        }
+        return device->WriteOkay("");
+    }
 
     // args[0] is command name, args[1] is variable.
     auto found_variable = kVariableMap.find(args[1]);
     if (found_variable == kVariableMap.end()) {
-        return device->WriteStatus(FastbootResult::FAIL, "Unknown variable");
+        return device->WriteFail("Unknown variable");
     }
 
+    std::string message;
     std::vector<std::string> getvar_args(args.begin() + 2, args.end());
-    return found_variable->second(device, getvar_args);
+    if (!found_variable->second.get(device, getvar_args, &message)) {
+        return device->WriteFail(message);
+    }
+    return device->WriteOkay(message);
 }
 
 bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {
@@ -86,6 +136,24 @@
     return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
 }
 
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
+    }
+
+    Result ret;
+    auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
+    if (!ret_val.isOk()) {
+        return device->WriteStatus(FastbootResult::FAIL, "Unable to do OEM command");
+    }
+    if (ret.status != Status::SUCCESS) {
+        return device->WriteStatus(FastbootResult::FAIL, ret.message);
+    }
+
+    return device->WriteStatus(FastbootResult::OKAY, ret.message);
+}
+
 bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
@@ -336,3 +404,11 @@
     }
     return device->WriteOkay("Partition resized");
 }
+
+bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteFail("Invalid arguments");
+    }
+    bool wipe = (args.size() >= 3 && args[2] == "wipe");
+    return UpdateSuper(device, args[1], wipe);
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index f67df91..9df43a9 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -44,3 +44,5 @@
 bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index c306e67..6862741 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -19,7 +19,7 @@
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
-
+#include <android/hardware/fastboot/1.0/IFastboot.h>
 #include <algorithm>
 
 #include "constants.h"
@@ -29,6 +29,7 @@
 using ::android::hardware::hidl_string;
 using ::android::hardware::boot::V1_0::IBootControl;
 using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::IFastboot;
 namespace sph = std::placeholders;
 
 FastbootDevice::FastbootDevice()
@@ -46,9 +47,12 @@
               {FB_CMD_CREATE_PARTITION, CreatePartitionHandler},
               {FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
               {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
+              {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
+              {FB_CMD_OEM, OemCmdHandler},
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
-      boot_control_hal_(IBootControl::getService()) {}
+      boot_control_hal_(IBootControl::getService()),
+      fastboot_hal_(IFastboot::getService()) {}
 
 FastbootDevice::~FastbootDevice() {
     CloseDevice();
@@ -117,10 +121,20 @@
         command[bytes_read] = '\0';
 
         LOG(INFO) << "Fastboot command: " << command;
-        auto args = android::base::Split(command, ":");
-        auto found_command = kCommandMap.find(args[0]);
+
+        std::vector<std::string> args;
+        std::string cmd_name;
+        if (android::base::StartsWith(command, "oem ")) {
+            args = {command};
+            cmd_name = "oem";
+        } else {
+            args = android::base::Split(command, ":");
+            cmd_name = args[0];
+        }
+
+        auto found_command = kCommandMap.find(cmd_name);
         if (found_command == kCommandMap.end()) {
-            WriteStatus(FastbootResult::FAIL, "Unrecognized command");
+            WriteStatus(FastbootResult::FAIL, "Unrecognized command " + args[0]);
             continue;
         }
         if (!found_command->second(this, args)) {
@@ -136,3 +150,7 @@
 bool FastbootDevice::WriteFail(const std::string& message) {
     return WriteStatus(FastbootResult::FAIL, message);
 }
+
+bool FastbootDevice::WriteInfo(const std::string& message) {
+    return WriteStatus(FastbootResult::INFO, message);
+}
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index addc2ef..189cf80 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/fastboot/1.0/IFastboot.h>
 
 #include "commands.h"
 #include "transport.h"
@@ -39,20 +40,25 @@
     bool HandleData(bool read, std::vector<char>* data);
     std::string GetCurrentSlot();
 
-    // Shortcuts for writing OKAY and FAIL status results.
+    // Shortcuts for writing status results.
     bool WriteOkay(const std::string& message);
     bool WriteFail(const std::string& message);
+    bool WriteInfo(const std::string& message);
 
     std::vector<char>& download_data() { return download_data_; }
     Transport* get_transport() { return transport_.get(); }
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
         return boot_control_hal_;
     }
+    android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
+        return fastboot_hal_;
+    }
 
   private:
     const std::unordered_map<std::string, CommandHandler> kCommandMap;
 
     std::unique_ptr<Transport> transport_;
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+    android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
     std::vector<char> download_data_;
 };
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index b5ed170..a383c54 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -25,6 +25,8 @@
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <ext4_utils/ext4_utils.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
 #include <sparse/sparse.h>
 
 #include "fastboot_device.h"
@@ -36,6 +38,8 @@
 
 }  // namespace
 
+using namespace android::fs_mgr;
+
 int FlashRawDataChunk(int fd, const char* data, size_t len) {
     size_t ret = 0;
     while (ret < len) {
@@ -68,7 +72,7 @@
 }
 
 int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
-    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false);
+    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, true);
     if (!file) {
         return -ENOENT;
     }
@@ -99,3 +103,74 @@
     }
     return FlashBlockDevice(handle.fd(), data);
 }
+
+bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe) {
+    std::optional<std::string> super = FindPhysicalPartition(partition_name);
+    if (!super) {
+        return device->WriteFail("Could not find partition: " + partition_name);
+    }
+
+    std::vector<char> data = std::move(device->download_data());
+    if (data.empty()) {
+        return device->WriteFail("No data available");
+    }
+
+    std::unique_ptr<LpMetadata> new_metadata = ReadFromImageBlob(data.data(), data.size());
+    if (!new_metadata) {
+        return device->WriteFail("Data is not a valid logical partition metadata image");
+    }
+
+    // If we are unable to read the existing metadata, then the super partition
+    // is corrupt. In this case we reflash the whole thing using the provided
+    // image.
+    std::string slot_suffix = device->GetCurrentSlot();
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(super->c_str(), slot_number);
+    if (!metadata || wipe) {
+        if (!FlashPartitionTable(super.value(), *new_metadata.get())) {
+            return device->WriteFail("Unable to flash new partition table");
+        }
+        return device->WriteOkay("Successfully flashed partition table");
+    }
+
+    // There's a working super partition, and we don't want to wipe it - it may
+    // may contain partitions created for the user. Instead, we create a zero-
+    // sized partition for each entry in the new partition table. It is then
+    // the host's responsibility to size it correctly via resize-logical-partition.
+    std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*metadata.get());
+    if (!builder) {
+        return device->WriteFail("Unable to create a metadata builder");
+    }
+    for (const auto& partition : new_metadata->partitions) {
+        std::string name = GetPartitionName(partition);
+        if (builder->FindPartition(name)) {
+            continue;
+        }
+        std::string guid = GetPartitionGuid(partition);
+        if (!builder->AddPartition(name, guid, partition.attributes)) {
+            return device->WriteFail("Unable to add partition: " + name);
+        }
+    }
+
+    // The scratch partition may exist as temporary storage, created for
+    // use by adb remount for overlayfs. If we're performing a flashall
+    // operation then we want to start over with a clean slate, so we
+    // remove the scratch partition until it is requested again.
+    builder->RemovePartition("scratch");
+
+    new_metadata = builder->Export();
+    if (!new_metadata) {
+        return device->WriteFail("Unable to export new partition table");
+    }
+
+    // Write the new table to every metadata slot.
+    bool ok = true;
+    for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
+        ok &= UpdatePartitionTable(super.value(), *new_metadata.get(), i);
+    }
+
+    if (!ok) {
+        return device->WriteFail("Unable to write new partition table");
+    }
+    return device->WriteOkay("Successfully updated partition table");
+}
diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h
index 206a407..89e20fc 100644
--- a/fastboot/device/flashing.h
+++ b/fastboot/device/flashing.h
@@ -22,3 +22,4 @@
 class FastbootDevice;
 
 int Flash(FastbootDevice* device, const std::string& partition_name);
+bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe);
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 215f99a..fb51a90 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -187,18 +187,9 @@
 
 // Reimplementing since usb_ffs_close() does not close the control FD.
 static void CloseFunctionFs(usb_handle* h) {
-    if (h->bulk_in > 0) {
-        close(h->bulk_in);
-        h->bulk_in = -1;
-    }
-    if (h->bulk_out > 0) {
-        close(h->bulk_out);
-        h->bulk_out = -1;
-    }
-    if (h->control > 0) {
-        close(h->control);
-        h->control = -1;
-    }
+    h->bulk_in.reset();
+    h->bulk_out.reset();
+    h->control.reset();
 }
 
 static bool InitFunctionFs(usb_handle* h) {
@@ -206,19 +197,19 @@
 
     if (h->control < 0) {  // might have already done this before
         LOG(INFO) << "opening control endpoint " << kUsbFfsFastbootEp0;
-        h->control = open(kUsbFfsFastbootEp0, O_RDWR);
+        h->control.reset(open(kUsbFfsFastbootEp0, O_RDWR));
         if (h->control < 0) {
             PLOG(ERROR) << "cannot open control endpoint " << kUsbFfsFastbootEp0;
             goto err;
         }
 
-        auto ret = write(h->control, &v2_descriptor, sizeof(v2_descriptor));
+        auto ret = write(h->control.get(), &v2_descriptor, sizeof(v2_descriptor));
         if (ret < 0) {
             PLOG(ERROR) << "cannot write descriptors " << kUsbFfsFastbootEp0;
             goto err;
         }
 
-        ret = write(h->control, &strings, sizeof(strings));
+        ret = write(h->control.get(), &strings, sizeof(strings));
         if (ret < 0) {
             PLOG(ERROR) << "cannot write strings " << kUsbFfsFastbootEp0;
             goto err;
@@ -227,20 +218,20 @@
         android::base::SetProperty("sys.usb.ffs.ready", "1");
     }
 
-    h->bulk_out = open(kUsbFfsFastbootOut, O_RDONLY);
+    h->bulk_out.reset(open(kUsbFfsFastbootOut, O_RDONLY));
     if (h->bulk_out < 0) {
         PLOG(ERROR) << "cannot open bulk-out endpoint " << kUsbFfsFastbootOut;
         goto err;
     }
 
-    h->bulk_in = open(kUsbFfsFastbootIn, O_WRONLY);
+    h->bulk_in.reset(open(kUsbFfsFastbootIn, O_WRONLY));
     if (h->bulk_in < 0) {
         PLOG(ERROR) << "cannot open bulk-in endpoint " << kUsbFfsFastbootIn;
         goto err;
     }
 
-    h->read_aiob.fd = h->bulk_out;
-    h->write_aiob.fd = h->bulk_in;
+    h->read_aiob.fd = h->bulk_out.get();
+    h->write_aiob.fd = h->bulk_in.get();
     h->reads_zero_packets = false;
     return true;
 
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index ec84576..261a202 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -16,6 +16,11 @@
 
 #include "utility.h"
 
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #include <android-base/logging.h>
 #include <fs_mgr_dm_linear.h>
 #include <liblp/liblp.h>
@@ -23,6 +28,7 @@
 #include "fastboot_device.h"
 
 using namespace android::fs_mgr;
+using namespace std::chrono_literals;
 using android::base::unique_fd;
 using android::hardware::boot::V1_0::Slot;
 
@@ -43,11 +49,11 @@
     }
     uint32_t slot_number = SlotNumberForSlotSuffix(slot);
     std::string dm_path;
-    if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, &dm_path)) {
+    if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, 5s, &dm_path)) {
         LOG(ERROR) << "Could not map partition: " << name;
         return false;
     }
-    auto closer = [name]() -> void { DestroyLogicalPartition(name); };
+    auto closer = [name]() -> void { DestroyLogicalPartition(name, 5s); };
     *handle = PartitionHandle(dm_path, std::move(closer));
     return true;
 }
@@ -75,7 +81,7 @@
 
 std::optional<std::string> FindPhysicalPartition(const std::string& name) {
     std::string path = "/dev/block/by-name/" + name;
-    if (access(path.c_str(), R_OK | W_OK) < 0) {
+    if (access(path.c_str(), W_OK) < 0) {
         return {};
     }
     return path;
@@ -123,3 +129,33 @@
     *number = slot[0] - 'a';
     return true;
 }
+
+std::vector<std::string> ListPartitions(FastbootDevice* device) {
+    std::vector<std::string> partitions;
+
+    // First get physical partitions.
+    struct dirent* de;
+    std::unique_ptr<DIR, decltype(&closedir)> by_name(opendir("/dev/block/by-name"), closedir);
+    while ((de = readdir(by_name.get())) != nullptr) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+            continue;
+        }
+        struct stat s;
+        std::string path = "/dev/block/by-name/" + std::string(de->d_name);
+        if (!stat(path.c_str(), &s) && S_ISBLK(s.st_mode)) {
+            partitions.emplace_back(de->d_name);
+        }
+    }
+
+    // Next get logical partitions.
+    if (auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME)) {
+        uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+        if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
+            for (const auto& partition : metadata->partitions) {
+                std::string partition_name = GetPartitionName(partition);
+                partitions.emplace_back(partition_name);
+            }
+        }
+    }
+    return partitions;
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 0931fc3..4f0d079 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -56,5 +56,5 @@
 bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
                             bool* is_zero_length = nullptr);
 bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
-
 bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
+std::vector<std::string> ListPartitions(FastbootDevice* device);
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 65cfea3..7535248 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -31,145 +31,278 @@
 
 using ::android::hardware::boot::V1_0::BoolResult;
 using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::fastboot::V1_0::FileSystemType;
+using ::android::hardware::fastboot::V1_0::Result;
+using ::android::hardware::fastboot::V1_0::Status;
 
 constexpr int kMaxDownloadSizeDefault = 0x20000000;
 constexpr char kFastbootProtocolVersion[] = "0.4";
 
-bool GetVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay(kFastbootProtocolVersion);
+bool GetVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                std::string* message) {
+    *message = kFastbootProtocolVersion;
+    return true;
 }
 
-bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay(android::base::GetProperty("ro.bootloader", ""));
+bool GetBootloaderVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                          std::string* message) {
+    *message = android::base::GetProperty("ro.bootloader", "");
+    return true;
 }
 
-bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay(android::base::GetProperty("ro.build.expect.baseband", ""));
+bool GetBasebandVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                        std::string* message) {
+    *message = android::base::GetProperty("ro.build.expect.baseband", "");
+    return true;
 }
 
-bool GetProduct(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay(android::base::GetProperty("ro.product.device", ""));
+bool GetProduct(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                std::string* message) {
+    *message = android::base::GetProperty("ro.product.device", "");
+    return true;
 }
 
-bool GetSerial(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay(android::base::GetProperty("ro.serialno", ""));
+bool GetSerial(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+               std::string* message) {
+    *message = android::base::GetProperty("ro.serialno", "");
+    return true;
 }
 
-bool GetSecure(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay(android::base::GetBoolProperty("ro.secure", "") ? "yes" : "no");
+bool GetSecure(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+               std::string* message) {
+    *message = android::base::GetBoolProperty("ro.secure", "") ? "yes" : "no";
+    return true;
 }
 
-bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                    std::string* message) {
     std::string suffix = device->GetCurrentSlot();
-    std::string slot = suffix.size() == 2 ? suffix.substr(1) : suffix;
-    return device->WriteOkay(slot);
+    *message = suffix.size() == 2 ? suffix.substr(1) : suffix;
+    return true;
 }
 
-bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& /* args */) {
+bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                  std::string* message) {
     auto boot_control_hal = device->boot_control_hal();
     if (!boot_control_hal) {
-        return "0";
+        *message = "0";
+    } else {
+        *message = std::to_string(boot_control_hal->getNumberSlots());
     }
-    return device->WriteOkay(std::to_string(boot_control_hal->getNumberSlots()));
+    return true;
 }
 
-bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message) {
     if (args.empty()) {
-        return device->WriteFail("Missing argument");
+        *message = "Missing argument";
+        return false;
     }
     Slot slot;
     if (!GetSlotNumber(args[0], &slot)) {
-        return device->WriteFail("Invalid slot");
+        *message = "Invalid slot";
+        return false;
     }
     auto boot_control_hal = device->boot_control_hal();
     if (!boot_control_hal) {
-        return device->WriteFail("Device has no slots");
+        *message = "Device has no slots";
+        return false;
     }
     if (boot_control_hal->isSlotMarkedSuccessful(slot) != BoolResult::TRUE) {
-        return device->WriteOkay("no");
+        *message = "no";
+    } else {
+        *message = "yes";
     }
-    return device->WriteOkay("yes");
+    return true;
 }
 
-bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message) {
     if (args.empty()) {
-        return device->WriteFail("Missing argument");
+        *message = "Missing argument";
+        return false;
     }
     Slot slot;
     if (!GetSlotNumber(args[0], &slot)) {
-        return device->WriteFail("Invalid slot");
+        *message = "Invalid slot";
+        return false;
     }
     auto boot_control_hal = device->boot_control_hal();
     if (!boot_control_hal) {
-        return device->WriteFail("Device has no slots");
+        *message = "Device has no slots";
+        return false;
     }
     if (boot_control_hal->isSlotBootable(slot) != BoolResult::TRUE) {
-        return device->WriteOkay("yes");
+        *message = "yes";
+    } else {
+        *message = "no";
     }
-    return device->WriteOkay("no");
+    return true;
 }
 
-bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay(std::to_string(kMaxDownloadSizeDefault));
+bool GetMaxDownloadSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                        std::string* message) {
+    *message = android::base::StringPrintf("0x%X", kMaxDownloadSizeDefault);
+    return true;
 }
 
-bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay("yes");
+bool GetUnlocked(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                 std::string* message) {
+    *message = "yes";
+    return true;
 }
 
-bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args,
+                std::string* message) {
     if (args.empty()) {
-        return device->WriteFail("Missing argument");
+        *message = "Missing argument";
+        return false;
     }
     std::string slot_suffix = device->GetCurrentSlot();
     if (slot_suffix.empty()) {
-        return device->WriteOkay("no");
+        *message = "no";
+        return true;
     }
     std::string partition_name = args[0] + slot_suffix;
     if (FindPhysicalPartition(partition_name) ||
         LogicalPartitionExists(partition_name, slot_suffix)) {
-        return device->WriteOkay("yes");
+        *message = "yes";
+    } else {
+        *message = "no";
     }
-    return device->WriteOkay("no");
+    return true;
 }
 
-bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message) {
     if (args.size() < 1) {
-        return device->WriteFail("Missing argument");
+        *message = "Missing argument";
+        return false;
     }
     // Zero-length partitions cannot be created through device-mapper, so we
     // special case them here.
     bool is_zero_length;
     if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) &&
         is_zero_length) {
-        return device->WriteOkay("0");
+        *message = "0";
+        return true;
     }
     // Otherwise, open the partition as normal.
     PartitionHandle handle;
     if (!OpenPartition(device, args[0], &handle)) {
-        return device->WriteFail("Could not open partition");
+        *message = "Could not open partition";
+        return false;
     }
     uint64_t size = get_block_device_size(handle.fd());
-    return device->WriteOkay(android::base::StringPrintf("%" PRIX64, size));
+    *message = android::base::StringPrintf("0x%" PRIX64, size);
+    return true;
 }
 
-bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args) {
+bool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message) {
     if (args.size() < 1) {
-        return device->WriteFail("Missing argument");
+        *message = "Missing argument";
+        return false;
+    }
+    std::string partition_name = args[0];
+    auto fastboot_hal = device->fastboot_hal();
+    if (!fastboot_hal) {
+        *message = "Fastboot HAL not found";
+        return false;
+    }
+
+    FileSystemType type;
+    Result ret;
+    auto ret_val =
+            fastboot_hal->getPartitionType(args[0], [&](FileSystemType fs_type, Result result) {
+                type = fs_type;
+                ret = result;
+            });
+    if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+        *message = "Unable to retrieve partition type";
+    } else {
+        switch (type) {
+            case FileSystemType::RAW:
+                *message = "raw";
+                return true;
+            case FileSystemType::EXT4:
+                *message = "ext4";
+                return true;
+            case FileSystemType::F2FS:
+                *message = "f2fs";
+                return true;
+            default:
+                *message = "Unknown file system type";
+        }
+    }
+
+    return false;
+}
+
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message) {
+    if (args.size() < 1) {
+        *message = "Missing argument";
+        return false;
     }
     // Note: if a partition name is in both the GPT and the super partition, we
     // return "true", to be consistent with prefering to flash logical partitions
     // over physical ones.
     std::string partition_name = args[0];
     if (LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
-        return device->WriteOkay("yes");
+        *message = "yes";
+        return true;
     }
     if (FindPhysicalPartition(partition_name)) {
-        return device->WriteOkay("no");
+        *message = "no";
+        return true;
     }
-    return device->WriteFail("Partition not found");
+    *message = "Partition not found";
+    return false;
 }
 
-bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& /* args */) {
-    return device->WriteOkay("yes");
+bool GetIsUserspace(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                    std::string* message) {
+    *message = "yes";
+    return true;
+}
+
+std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device) {
+    std::vector<std::vector<std::string>> args;
+    auto partitions = ListPartitions(device);
+    for (const auto& partition : partitions) {
+        args.emplace_back(std::initializer_list<std::string>{partition});
+    }
+    return args;
+}
+
+std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device) {
+    auto partitions = ListPartitions(device);
+
+    std::string slot_suffix = device->GetCurrentSlot();
+    if (!slot_suffix.empty()) {
+        auto names = std::move(partitions);
+        for (const auto& name : names) {
+            std::string slotless_name = name;
+            if (android::base::EndsWith(name, "_a") || android::base::EndsWith(name, "_b")) {
+                slotless_name = name.substr(0, name.rfind("_"));
+            }
+            if (std::find(partitions.begin(), partitions.end(), slotless_name) ==
+                partitions.end()) {
+                partitions.emplace_back(slotless_name);
+            }
+        }
+    }
+
+    std::vector<std::vector<std::string>> args;
+    for (const auto& partition : partitions) {
+        args.emplace_back(std::initializer_list<std::string>{partition});
+    }
+    return args;
+}
+
+bool GetHardwareRevision(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                         std::string* message) {
+    *message = android::base::GetProperty("ro.revision", "");
+    return true;
 }
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 554a080..63f2670 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -21,19 +21,38 @@
 
 class FastbootDevice;
 
-bool GetVersion(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args);
-bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetVersion(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& args,
+                          std::string* message);
+bool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args,
+                        std::string* message);
+bool GetProduct(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSerial(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetSecure(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& args,
+                    std::string* message);
+bool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& args,
+                  std::string* message);
+bool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message);
+bool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args,
+                       std::string* message);
+bool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& args,
+                        std::string* message);
+bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& args,
+                 std::string* message);
+bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
+bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message);
+bool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& args,
+                      std::string* message);
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args,
+                           std::string* message);
+bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args,
+                    std::string* message);
+bool GetHardwareRevision(FastbootDevice* device, const std::vector<std::string>& args,
+                         std::string* message);
+
+// Helpers for getvar all.
+std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
+std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 6890643..a43e7a6 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -44,39 +44,8 @@
 #include "constants.h"
 #include "transport.h"
 
-enum Op {
-    OP_DOWNLOAD,
-    OP_COMMAND,
-    OP_QUERY,
-    OP_NOTICE,
-    OP_DOWNLOAD_SPARSE,
-    OP_WAIT_FOR_DISCONNECT,
-    OP_DOWNLOAD_FD,
-    OP_UPLOAD,
-};
+using android::base::StringPrintf;
 
-struct Action {
-    Action(Op op, const std::string& cmd) : op(op), cmd(cmd) {}
-
-    Op op;
-    std::string cmd;
-    std::string msg;
-
-    std::string product;
-
-    void* data = nullptr;
-    // The protocol only supports 32-bit sizes, so you'll have to break
-    // anything larger into multiple chunks.
-    uint32_t size = 0;
-
-    int fd = -1;
-
-    int (*func)(Action& a, int status, const char* resp) = nullptr;
-
-    double start = -1;
-};
-
-static std::vector<std::unique_ptr<Action>> action_list;
 static fastboot::FastBootDriver* fb = nullptr;
 
 void fb_init(fastboot::FastBootDriver& fbi) {
@@ -85,6 +54,12 @@
     fb->SetInfoCallback(cb);
 }
 
+void fb_reinit(Transport* transport) {
+    if (Transport* old_transport = fb->set_transport(transport)) {
+        delete old_transport;
+    }
+}
+
 const std::string fb_get_error() {
     return fb->Error();
 }
@@ -93,282 +68,130 @@
     return !fb->GetVar(key, value);
 }
 
-static int cb_default(Action& a, int status, const char* resp) {
+static void HandleResult(double start, int status) {
     if (status) {
-        fprintf(stderr,"FAILED (%s)\n", resp);
+        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+        die("Command failed");
     } else {
         double split = now();
-        fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
-        a.start = split;
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
     }
-    return status;
 }
 
-static Action& queue_action(Op op, const std::string& cmd) {
-    std::unique_ptr<Action> a{new Action(op, cmd)};
-    a->func = cb_default;
-
-    action_list.push_back(std::move(a));
-    return *action_list.back();
-}
+#define RUN_COMMAND(command)         \
+    {                                \
+        double start = now();        \
+        auto status = (command);     \
+        HandleResult(start, status); \
+    }
 
 void fb_set_active(const std::string& slot) {
-    Action& a = queue_action(OP_COMMAND, FB_CMD_SET_ACTIVE ":" + slot);
-    a.msg = "Setting current slot to '" + slot + "'";
+    Status("Setting current slot to '" + slot + "'");
+    RUN_COMMAND(fb->SetActive(slot));
 }
 
-void fb_queue_erase(const std::string& partition) {
-    Action& a = queue_action(OP_COMMAND, FB_CMD_ERASE ":" + partition);
-    a.msg = "Erasing '" + partition + "'";
+void fb_erase(const std::string& partition) {
+    Status("Erasing '" + partition + "'");
+    RUN_COMMAND(fb->Erase(partition));
 }
 
-void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz) {
-    Action& a = queue_action(OP_DOWNLOAD_FD, "");
-    a.fd = fd;
-    a.size = sz;
-    a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
+void fb_flash_fd(const std::string& partition, int fd, uint32_t sz) {
+    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
+    RUN_COMMAND(fb->Download(fd, sz));
 
-    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
-    b.msg = "Writing '" + partition + "'";
+    Status("Writing '" + partition + "'");
+    RUN_COMMAND(fb->Flash(partition));
 }
 
-void fb_queue_flash(const std::string& partition, void* data, uint32_t sz) {
-    Action& a = queue_action(OP_DOWNLOAD, "");
-    a.data = data;
-    a.size = sz;
-    a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024);
+void fb_flash(const std::string& partition, const std::vector<char>& data) {
+    Status(StringPrintf("Sending '%s' (%zu KB)", partition.c_str(), data.size() / 1024));
+    RUN_COMMAND(fb->Download(data));
 
-    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
-    b.msg = "Writing '" + partition + "'";
+    Status("Writing '" + partition + "'");
+    RUN_COMMAND(fb->Flash(partition));
 }
 
-void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
-                           size_t current, size_t total) {
-    Action& a = queue_action(OP_DOWNLOAD_SPARSE, "");
-    a.data = s;
-    a.size = 0;
-    a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(),
-                                        current, total, sz / 1024);
+void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
+                     size_t current, size_t total) {
+    Status(StringPrintf("Sending sparse '%s' %zu/%zu (%u KB)", partition.c_str(), current, total,
+                        sz / 1024));
+    RUN_COMMAND(fb->Download(s));
 
-    Action& b = queue_action(OP_COMMAND, FB_CMD_FLASH ":" + partition);
-    b.msg = android::base::StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current,
-                                        total);
+    Status(StringPrintf("Writing sparse '%s' %zu/%zu", partition.c_str(), current, total));
+    RUN_COMMAND(fb->Flash(partition));
 }
 
-void fb_queue_create_partition(const std::string& partition, const std::string& size) {
-    Action& a = queue_action(OP_COMMAND, FB_CMD_CREATE_PARTITION ":" + partition + ":" + size);
-    a.msg = "Creating '" + partition + "'";
+void fb_create_partition(const std::string& partition, const std::string& size) {
+    Status("Creating '" + partition + "'");
+    RUN_COMMAND(fb->RawCommand(FB_CMD_CREATE_PARTITION ":" + partition + ":" + size));
 }
 
-void fb_queue_delete_partition(const std::string& partition) {
-    Action& a = queue_action(OP_COMMAND, FB_CMD_DELETE_PARTITION ":" + partition);
-    a.msg = "Deleting '" + partition + "'";
+void fb_delete_partition(const std::string& partition) {
+    Status("Deleting '" + partition + "'");
+    RUN_COMMAND(fb->RawCommand(FB_CMD_DELETE_PARTITION ":" + partition));
 }
 
-void fb_queue_resize_partition(const std::string& partition, const std::string& size) {
-    Action& a = queue_action(OP_COMMAND, FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size);
-    a.msg = "Resizing '" + partition + "'";
+void fb_resize_partition(const std::string& partition, const std::string& size) {
+    Status("Resizing '" + partition + "'");
+    RUN_COMMAND(fb->RawCommand(FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size));
 }
 
-static int match(const char* str, const char** value, unsigned count) {
-    unsigned n;
-
-    for (n = 0; n < count; n++) {
-        const char *val = value[n];
-        int len = strlen(val);
-        int match;
-
-        if ((len > 1) && (val[len-1] == '*')) {
-            len--;
-            match = !strncmp(val, str, len);
-        } else {
-            match = !strcmp(val, str);
-        }
-
-        if (match) return 1;
-    }
-
-    return 0;
-}
-
-static int cb_check(Action& a, int status, const char* resp, int invert) {
-    const char** value = reinterpret_cast<const char**>(a.data);
-    unsigned count = a.size;
-    unsigned n;
+void fb_display(const std::string& label, const std::string& var) {
+    std::string value;
+    auto status = fb->GetVar(var, &value);
 
     if (status) {
-        fprintf(stderr,"FAILED (%s)\n", resp);
-        return status;
+        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
+        return;
     }
-
-    if (!a.product.empty()) {
-        if (a.product != cur_product) {
-            double split = now();
-            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product,
-                    a.product.c_str(), (split - a.start));
-            a.start = split;
-            return 0;
-        }
-    }
-
-    int yes = match(resp, value, count);
-    if (invert) yes = !yes;
-
-    if (yes) {
-        double split = now();
-        fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
-        a.start = split;
-        return 0;
-    }
-
-    fprintf(stderr, "FAILED\n\n");
-    fprintf(stderr, "Device %s is '%s'.\n", a.cmd.c_str() + 7, resp);
-    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", value[0]);
-    for (n = 1; n < count; n++) {
-        fprintf(stderr, " or '%s'", value[n]);
-    }
-    fprintf(stderr, ".\n\n");
-    return -1;
+    fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
 }
 
-static int cb_require(Action& a, int status, const char* resp) {
-    return cb_check(a, status, resp, 0);
-}
-
-static int cb_reject(Action& a, int status, const char* resp) {
-    return cb_check(a, status, resp, 1);
-}
-
-void fb_queue_require(const std::string& product, const std::string& var, bool invert,
-                      size_t nvalues, const char** values) {
-    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
-    a.product = product;
-    a.data = values;
-    a.size = nvalues;
-    a.msg = "Checking " + var;
-    a.func = invert ? cb_reject : cb_require;
-    if (a.data == nullptr) die("out of memory");
-}
-
-static int cb_display(Action& a, int status, const char* resp) {
-    if (status) {
-        fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
-        return status;
-    }
-    fprintf(stderr, "%s: %s\n", static_cast<const char*>(a.data), resp);
-    free(static_cast<char*>(a.data));
-    return 0;
-}
-
-void fb_queue_display(const std::string& label, const std::string& var) {
-    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
-    a.data = xstrdup(label.c_str());
-    a.func = cb_display;
-}
-
-static int cb_save(Action& a, int status, const char* resp) {
-    if (status) {
-        fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
-        return status;
-    }
-    strncpy(reinterpret_cast<char*>(a.data), resp, a.size);
-    return 0;
-}
-
-void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) {
-    Action& a = queue_action(OP_QUERY, FB_CMD_GETVAR ":" + var);
-    a.data = dest;
-    a.size = dest_size;
-    a.func = cb_save;
-}
-
-static int cb_do_nothing(Action&, int, const char*) {
+void fb_reboot() {
+    fprintf(stderr, "Rebooting");
+    fb->Reboot();
     fprintf(stderr, "\n");
-    return 0;
 }
 
-void fb_queue_reboot() {
-    Action& a = queue_action(OP_COMMAND, FB_CMD_REBOOT);
-    a.func = cb_do_nothing;
-    a.msg = "Rebooting";
+void fb_command(const std::string& cmd, const std::string& msg) {
+    Status(msg);
+    RUN_COMMAND(fb->RawCommand(cmd));
 }
 
-void fb_queue_command(const std::string& cmd, const std::string& msg) {
-    Action& a = queue_action(OP_COMMAND, cmd);
-    a.msg = msg;
+void fb_download(const std::string& name, const std::vector<char>& data) {
+    Status("Downloading '" + name + "'");
+    RUN_COMMAND(fb->Download(data));
 }
 
-void fb_queue_download(const std::string& name, void* data, uint32_t size) {
-    Action& a = queue_action(OP_DOWNLOAD, "");
-    a.data = data;
-    a.size = size;
-    a.msg = "Downloading '" + name + "'";
+void fb_download_fd(const std::string& name, int fd, uint32_t sz) {
+    Status(StringPrintf("Sending '%s' (%u KB)", name.c_str(), sz / 1024));
+    RUN_COMMAND(fb->Download(fd, sz));
 }
 
-void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz) {
-    Action& a = queue_action(OP_DOWNLOAD_FD, "");
-    a.fd = fd;
-    a.size = sz;
-    a.msg = android::base::StringPrintf("Sending '%s' (%u KB)", name.c_str(), sz / 1024);
+void fb_upload(const std::string& outfile) {
+    Status("Uploading '" + outfile + "'");
+    RUN_COMMAND(fb->Upload(outfile));
 }
 
-void fb_queue_upload(const std::string& outfile) {
-    Action& a = queue_action(OP_UPLOAD, "");
-    a.data = xstrdup(outfile.c_str());
-    a.msg = "Uploading '" + outfile + "'";
+void fb_notice(const std::string& notice) {
+    Status(notice);
+    fprintf(stderr, "\n");
 }
 
-void fb_queue_notice(const std::string& notice) {
-    Action& a = queue_action(OP_NOTICE, "");
-    a.msg = notice;
+void fb_wait_for_disconnect() {
+    fb->WaitForDisconnect();
 }
 
-void fb_queue_wait_for_disconnect() {
-    queue_action(OP_WAIT_FOR_DISCONNECT, "");
-}
+bool fb_reboot_to_userspace() {
+    Status("Rebooting to userspace fastboot");
+    verbose("\n");
 
-int64_t fb_execute_queue() {
-    int64_t status = 0;
-    for (auto& a : action_list) {
-        a->start = now();
-        if (!a->msg.empty()) {
-            fprintf(stderr, "%-50s ", a->msg.c_str());
-            verbose("\n");
-        }
-        if (a->op == OP_DOWNLOAD) {
-            char* cbuf = static_cast<char*>(a->data);
-            status = fb->Download(cbuf, a->size);
-            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
-            if (status) break;
-        } else if (a->op == OP_DOWNLOAD_FD) {
-            status = fb->Download(a->fd, a->size);
-            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
-            if (status) break;
-        } else if (a->op == OP_COMMAND) {
-            status = fb->RawCommand(a->cmd);
-            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
-            if (status) break;
-        } else if (a->op == OP_QUERY) {
-            std::string resp;
-            status = fb->RawCommand(a->cmd, &resp);
-            status = a->func(*a, status, status ? fb_get_error().c_str() : resp.c_str());
-            if (status) break;
-        } else if (a->op == OP_NOTICE) {
-            // We already showed the notice because it's in `Action::msg`.
-            fprintf(stderr, "\n");
-        } else if (a->op == OP_DOWNLOAD_SPARSE) {
-            status = fb->Download(reinterpret_cast<sparse_file*>(a->data));
-            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
-            if (status) break;
-        } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
-            fb->WaitForDisconnect();
-        } else if (a->op == OP_UPLOAD) {
-            status = fb->Upload(reinterpret_cast<const char*>(a->data));
-            status = a->func(*a, status, status ? fb_get_error().c_str() : "");
-        } else {
-            die("unknown action: %d", a->op);
-        }
+    if (fb->RebootTo("fastboot") != fastboot::RetCode::SUCCESS) {
+        fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+        return false;
     }
-    action_list.clear();
-    return status;
+    fprintf(stderr, "OKAY\n");
+
+    fb_reinit(nullptr);
+    return true;
 }
diff --git a/fastboot/engine.h b/fastboot/engine.h
index 8aebdd7..b681f5a 100644
--- a/fastboot/engine.h
+++ b/fastboot/engine.h
@@ -44,36 +44,28 @@
 
 const std::string fb_get_error();
 
-//#define FB_COMMAND_SZ (fastboot::FB_COMMAND_SZ)
-//#define FB_RESPONSE_SZ (fastboot::FB_RESPONSE_SZ)
-
-/* engine.c - high level command queue engine */
-
 void fb_init(fastboot::FastBootDriver& fbi);
+void fb_reinit(Transport* transport);
 
 bool fb_getvar(const std::string& key, std::string* value);
-void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
-void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz);
-void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
-                           size_t current, size_t total);
-void fb_queue_erase(const std::string& partition);
-void fb_queue_format(const std::string& partition, int skip_if_not_supported, int32_t max_chunk_sz);
-void fb_queue_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
-                      const char** values);
-void fb_queue_display(const std::string& label, const std::string& var);
-void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size);
-void fb_queue_reboot(void);
-void fb_queue_command(const std::string& cmd, const std::string& msg);
-void fb_queue_download(const std::string& name, void* data, uint32_t size);
-void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz);
-void fb_queue_upload(const std::string& outfile);
-void fb_queue_notice(const std::string& notice);
-void fb_queue_wait_for_disconnect(void);
-void fb_queue_create_partition(const std::string& partition, const std::string& size);
-void fb_queue_delete_partition(const std::string& partition);
-void fb_queue_resize_partition(const std::string& partition, const std::string& size);
-int64_t fb_execute_queue();
+void fb_flash(const std::string& partition, const std::vector<char>& data);
+void fb_flash_fd(const std::string& partition, int fd, uint32_t sz);
+void fb_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
+                     size_t current, size_t total);
+void fb_erase(const std::string& partition);
+void fb_display(const std::string& label, const std::string& var);
+void fb_reboot();
+void fb_command(const std::string& cmd, const std::string& msg);
+void fb_download(const std::string& name, const std::vector<char>& data);
+void fb_download_fd(const std::string& name, int fd, uint32_t sz);
+void fb_upload(const std::string& outfile);
+void fb_notice(const std::string& notice);
+void fb_wait_for_disconnect(void);
+void fb_create_partition(const std::string& partition, const std::string& size);
+void fb_delete_partition(const std::string& partition);
+void fb_resize_partition(const std::string& partition, const std::string& size);
 void fb_set_active(const std::string& slot);
+bool fb_reboot_to_userspace();
 
 /* Current product */
 extern char cur_product[FB_RESPONSE_SZ + 1];
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index dc94952..5173bab 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,7 @@
 
 #include <chrono>
 #include <functional>
+#include <regex>
 #include <thread>
 #include <utility>
 #include <vector>
@@ -55,6 +56,8 @@
 #include <android-base/strings.h>
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
+#include <build/version.h>
+#include <platform_tools_version.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
@@ -67,14 +70,15 @@
 #include "udp.h"
 #include "usb.h"
 
+using android::base::ReadFully;
+using android::base::Split;
+using android::base::Trim;
 using android::base::unique_fd;
 
 #ifndef O_BINARY
 #define O_BINARY 0
 #endif
 
-char cur_product[FB_RESPONSE_SZ + 1];
-
 static const char* serial = nullptr;
 
 static bool g_long_listing = false;
@@ -104,45 +108,64 @@
     void* data;
     int64_t sz;
     int fd;
+    int64_t image_size;
 };
 
-static struct {
+enum class ImageType {
+    // Must be flashed for device to boot into the kernel.
+    BootCritical,
+    // Normal partition to be flashed during "flashall".
+    Normal,
+    // Partition that is never flashed during "flashall".
+    Extra
+};
+
+struct Image {
     const char* nickname;
     const char* img_name;
     const char* sig_name;
     const char* part_name;
     bool optional_if_no_image;
-    bool optional_if_no_partition;
+    ImageType type;
     bool IsSecondary() const { return nickname == nullptr; }
-} images[] = {
+};
+
+static Image images[] = {
         // clang-format off
-    { "boot",     "boot.img",         "boot.sig",     "boot",     false, false },
-    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  false },
-    { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  false },
-    { "dts",      "dt.img",           "dt.sig",       "dts",      true,  false },
-    { "odm",      "odm.img",          "odm.sig",      "odm",      true,  false },
-    { "product",  "product.img",      "product.sig",  "product",  true,  false },
-    { "product-services",
-                  "product-services.img",
-                                      "product-services.sig",
+    { "boot",     "boot.img",         "boot.sig",     "boot",     false, ImageType::BootCritical },
+    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  ImageType::Normal },
+    { "cache",    "cache.img",        "cache.sig",    "cache",    true,  ImageType::Extra },
+    { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  ImageType::BootCritical },
+    { "dts",      "dt.img",           "dt.sig",       "dts",      true,  ImageType::BootCritical },
+    { "odm",      "odm.img",          "odm.sig",      "odm",      true,  ImageType::Normal },
+    { "product",  "product.img",      "product.sig",  "product",  true,  ImageType::Normal },
+    { "product_services",
+                  "product_services.img",
+                                      "product_services.sig",
                                                       "product_services",
-                                                                  true,  true  },
-    { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  false },
-    { "super",    "super.img",        "super.sig",    "super",    true,  true  },
-    { "system",   "system.img",       "system.sig",   "system",   false, true  },
-    { nullptr,    "system_other.img", "system.sig",   "system",   true,  false },
-    { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  false },
-    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  true  },
-    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  false },
+                                                                  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 },
+    { nullptr,    "system_other.img", "system.sig",   "system",   true,  ImageType::Normal },
+    { "userdata", "userdata.img",     "userdata.sig", "userdata", true,  ImageType::Extra },
+    { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  ImageType::BootCritical },
+    { "vbmeta_mainline",
+                  "vbmeta_mainline.img",
+                                      "vbmeta_mainline.sig",
+                                                      "vbmeta_mainline",
+                                                                  true,  ImageType::BootCritical },
+    { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
+    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
 };
 
-static std::string find_item_given_name(const char* img_name) {
+static std::string find_item_given_name(const std::string& img_name) {
     char* dir = getenv("ANDROID_PRODUCT_OUT");
     if (dir == nullptr || dir[0] == '\0') {
         die("ANDROID_PRODUCT_OUT not set");
     }
-    return android::base::StringPrintf("%s/%s", dir, img_name);
+    return std::string(dir) + "/" + img_name;
 }
 
 static std::string find_item(const std::string& item) {
@@ -152,47 +175,28 @@
         }
     }
 
-    if (item == "userdata") return find_item_given_name("userdata.img");
-    if (item == "cache") return find_item_given_name("cache.img");
-
     fprintf(stderr, "unknown partition '%s'\n", item.c_str());
     return "";
 }
 
 static int64_t get_file_size(int fd) {
     struct stat sb;
-    return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
+    if (fstat(fd, &sb) == -1) {
+        die("could not get file size");
+    }
+    return sb.st_size;
 }
 
-static void* load_fd(int fd, int64_t* sz) {
-    int errno_tmp;
-    char* data = nullptr;
+bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
+    out->clear();
 
-    *sz = get_file_size(fd);
-    if (*sz < 0) {
-        goto oops;
+    unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)));
+    if (fd == -1) {
+        return false;
     }
 
-    data = (char*) malloc(*sz);
-    if (data == nullptr) goto oops;
-
-    if(read(fd, data, *sz) != *sz) goto oops;
-    close(fd);
-
-    return data;
-
-oops:
-    errno_tmp = errno;
-    close(fd);
-    if(data != 0) free(data);
-    errno = errno_tmp;
-    return 0;
-}
-
-static void* load_file(const std::string& path, int64_t* sz) {
-    int fd = open(path.c_str(), O_RDONLY | O_BINARY);
-    if (fd == -1) return nullptr;
-    return load_fd(fd, sz);
+    out->resize(get_file_size(fd));
+    return ReadFully(fd, out->data(), out->size());
 }
 
 static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
@@ -242,13 +246,8 @@
 // The returned Transport is a singleton, so multiple calls to this function will return the same
 // object, and the caller should not attempt to delete the returned Transport.
 static Transport* open_device() {
-    static Transport* transport = nullptr;
     bool announce = true;
 
-    if (transport != nullptr) {
-        return transport;
-    }
-
     Socket::Protocol protocol = Socket::Protocol::kTcp;
     std::string host;
     int port = 0;
@@ -273,6 +272,7 @@
         }
     }
 
+    Transport* transport = nullptr;
     while (true) {
         if (!host.empty()) {
             std::string error;
@@ -404,70 +404,71 @@
     return 0;
 }
 
-static void* load_bootable_image(const std::string& kernel, const std::string& ramdisk,
-                                 const std::string& second_stage, int64_t* sz) {
-    int64_t ksize;
-    void* kdata = load_file(kernel.c_str(), &ksize);
-    if (kdata == nullptr) die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+static std::vector<char> LoadBootableImage(const std::string& kernel, const std::string& ramdisk,
+                                           const std::string& second_stage) {
+    std::vector<char> kernel_data;
+    if (!ReadFileToVector(kernel, &kernel_data)) {
+        die("cannot load '%s': %s", kernel.c_str(), strerror(errno));
+    }
 
     // Is this actually a boot image?
-    if (ksize < static_cast<int64_t>(sizeof(boot_img_hdr_v1))) {
+    if (kernel_data.size() < sizeof(boot_img_hdr_v1)) {
         die("cannot load '%s': too short", kernel.c_str());
     }
-    if (!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
+    if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
         if (!g_cmdline.empty()) {
-            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kdata), g_cmdline);
+            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v1*>(kernel_data.data()), g_cmdline);
         }
 
         if (!ramdisk.empty()) die("cannot boot a boot.img *and* ramdisk");
 
-        *sz = ksize;
-        return kdata;
+        return kernel_data;
     }
 
-    void* rdata = nullptr;
-    int64_t rsize = 0;
+    std::vector<char> ramdisk_data;
     if (!ramdisk.empty()) {
-        rdata = load_file(ramdisk.c_str(), &rsize);
-        if (rdata == nullptr) die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        if (!ReadFileToVector(ramdisk, &ramdisk_data)) {
+            die("cannot load '%s': %s", ramdisk.c_str(), strerror(errno));
+        }
     }
 
-    void* sdata = nullptr;
-    int64_t ssize = 0;
+    std::vector<char> second_stage_data;
     if (!second_stage.empty()) {
-        sdata = load_file(second_stage.c_str(), &ssize);
-        if (sdata == nullptr) die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        if (!ReadFileToVector(second_stage, &second_stage_data)) {
+            die("cannot load '%s': %s", second_stage.c_str(), strerror(errno));
+        }
     }
-
     fprintf(stderr,"creating boot image...\n");
-    boot_img_hdr_v1* bdata = mkbootimg(kdata, ksize, rdata, rsize, sdata, ssize,
-                                       g_base_addr, g_boot_img_hdr, sz);
-    if (bdata == nullptr) die("failed to create boot.img");
 
-    if (!g_cmdline.empty()) bootimg_set_cmdline(bdata, g_cmdline);
-    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", *sz);
+    std::vector<char> out;
+    boot_img_hdr_v1* boot_image_data = mkbootimg(kernel_data, ramdisk_data, second_stage_data,
+                                                 g_base_addr, g_boot_img_hdr, &out);
 
-    return bdata;
+    if (!g_cmdline.empty()) bootimg_set_cmdline(boot_image_data, g_cmdline);
+    fprintf(stderr, "creating boot image - %zu bytes\n", out.size());
+
+    return out;
 }
 
-static void* unzip_to_memory(ZipArchiveHandle zip, const char* entry_name, int64_t* sz) {
-    ZipString zip_entry_name(entry_name);
+static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
+                          std::vector<char>* out) {
+    ZipString zip_entry_name(entry_name.c_str());
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
-        fprintf(stderr, "archive does not contain '%s'\n", entry_name);
-        return nullptr;
+        fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
+        return false;
     }
 
-    *sz = zip_entry.uncompressed_length;
+    out->resize(zip_entry.uncompressed_length);
 
-    fprintf(stderr, "extracting %s (%" PRId64 " MB) to RAM...\n", entry_name, *sz / 1024 / 1024);
-    uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
-    if (data == nullptr) die("failed to allocate %" PRId64 " bytes for '%s'", *sz, entry_name);
+    fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
+            out->size() / 1024 / 1024);
 
-    int error = ExtractToMemory(zip, &zip_entry, data, zip_entry.uncompressed_length);
-    if (error != 0) die("failed to extract '%s': %s", entry_name, ErrorCodeString(error));
+    int error = ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()),
+                                out->size());
+    if (error != 0) die("failed to extract '%s': %s", entry_name.c_str(), ErrorCodeString(error));
 
-    return data;
+    return true;
 }
 
 #if defined(_WIN32)
@@ -567,6 +568,7 @@
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
+        errno = ENOENT;
         return -1;
     }
 
@@ -587,122 +589,155 @@
     return fd.release();
 }
 
-static char* strip(char* s) {
-    while (*s && isspace(*s)) s++;
+static void CheckRequirement(const std::string& cur_product, const std::string& var,
+                             const std::string& product, bool invert,
+                             const std::vector<std::string>& options) {
+    Status("Checking '" + var + "'");
 
-    int n = strlen(s);
-    while (n-- > 0) {
-        if (!isspace(s[n])) break;
-        s[n] = 0;
+    double start = now();
+
+    if (!product.empty()) {
+        if (product != cur_product) {
+            double split = now();
+            fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n",
+                    cur_product.c_str(), product.c_str(), (split - start));
+            return;
+        }
     }
-    return s;
+
+    std::string var_value;
+    if (!fb_getvar(var, &var_value)) {
+        fprintf(stderr, "FAILED\n\n");
+        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(), fb_get_error().c_str());
+        die("requirements not met!");
+    }
+
+    bool match = false;
+    for (const auto& option : options) {
+        if (option == var_value || (option.back() == '*' &&
+                                    !var_value.compare(0, option.length() - 1, option, 0,
+                                                       option.length() - 1))) {
+            match = true;
+            break;
+        }
+    }
+
+    if (invert) {
+        match = !match;
+    }
+
+    if (match) {
+        double split = now();
+        fprintf(stderr, "OKAY [%7.3fs]\n", (split - start));
+        return;
+    }
+
+    fprintf(stderr, "FAILED\n\n");
+    fprintf(stderr, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
+    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", options[0].c_str());
+    for (auto it = std::next(options.begin()); it != options.end(); ++it) {
+        fprintf(stderr, " or '%s'", it->c_str());
+    }
+    fprintf(stderr, ".\n\n");
+    die("requirements not met!");
 }
 
-#define MAX_OPTIONS 32
-static void check_requirement(char* line) {
-    char *val[MAX_OPTIONS];
-    unsigned count;
-    char *x;
-    int invert = 0;
-
+bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                          bool* invert, std::vector<std::string>* options) {
     // "require product=alpha|beta|gamma"
     // "require version-bootloader=1234"
     // "require-for-product:gamma version-bootloader=istanbul|constantinople"
     // "require partition-exists=vendor"
+    *product = "";
+    *invert = false;
 
-    char* name = line;
-    const char* product = "";
-    if (!strncmp(name, "reject ", 7)) {
-        name += 7;
-        invert = 1;
-    } else if (!strncmp(name, "require ", 8)) {
-        name += 8;
-        invert = 0;
-    } else if (!strncmp(name, "require-for-product:", 20)) {
-        // Get the product and point name past it
-        product = name + 20;
-        name = strchr(name, ' ');
-        if (!name) die("android-info.txt syntax error: %s", line);
-        *name = 0;
-        name += 1;
-        invert = 0;
+    auto require_reject_regex = std::regex{"(require\\s+|reject\\s+)?\\s*(\\S+)\\s*=\\s*(.*)"};
+    auto require_product_regex =
+            std::regex{"require-for-product:\\s*(\\S+)\\s+(\\S+)\\s*=\\s*(.*)"};
+    std::smatch match_results;
+
+    if (std::regex_match(line, match_results, require_reject_regex)) {
+        *invert = Trim(match_results[1]) == "reject";
+    } else if (std::regex_match(line, match_results, require_product_regex)) {
+        *product = match_results[1];
+    } else {
+        return false;
     }
 
-    x = strchr(name, '=');
-    if (x == 0) return;
-    *x = 0;
-    val[0] = x + 1;
-
-    name = strip(name);
-
-    // "require partition-exists=x" is a special case, added because of the trouble we had when
-    // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
-    // missing out new partitions. A device with new partitions can use "partition-exists" to
-    // override the fields `optional_if_no_image` and 'optional_if_no_partition' in the `images`
-    // array.
-    if (!strcmp(name, "partition-exists")) {
-        const char* partition_name = val[0];
-        std::string has_slot;
-        if (!fb_getvar(std::string("has-slot:") + partition_name, &has_slot) ||
-            (has_slot != "yes" && has_slot != "no")) {
-            die("device doesn't have required partition %s!", partition_name);
-        }
-        bool known_partition = false;
-        for (size_t i = 0; i < arraysize(images); ++i) {
-            if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
-                images[i].optional_if_no_image = false;
-                images[i].optional_if_no_partition = false;
-                known_partition = true;
-            }
-        }
-        if (!known_partition) {
-            die("device requires partition %s which is not known to this version of fastboot",
-                partition_name);
-        }
-        return;
-    }
-
-    for(count = 1; count < MAX_OPTIONS; count++) {
-        x = strchr(val[count - 1],'|');
-        if (x == 0) break;
-        *x = 0;
-        val[count] = x + 1;
-    }
-
+    *name = match_results[2];
     // Work around an unfortunate name mismatch.
-    const char* var = name;
-    if (!strcmp(name, "board")) var = "product";
-
-    const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
-    if (out == nullptr) die("out of memory");
-
-    for (size_t i = 0; i < count; ++i) {
-        out[i] = xstrdup(strip(val[i]));
+    if (*name == "board") {
+        *name = "product";
     }
 
-    fb_queue_require(product, var, invert, count, out);
+    auto raw_options = Split(match_results[3], "|");
+    for (const auto& option : raw_options) {
+        auto trimmed_option = Trim(option);
+        options->emplace_back(trimmed_option);
+    }
+
+    return true;
 }
 
-static void check_requirements(char* data, int64_t sz) {
-    char* s = data;
-    while (sz-- > 0) {
-        if (*s == '\n') {
-            *s++ = 0;
-            check_requirement(data);
-            data = s;
-        } else {
-            s++;
+// "require partition-exists=x" is a special case, added because of the trouble we had when
+// Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
+// missing out new partitions. A device with new partitions can use "partition-exists" to
+// override the fields `optional_if_no_image` in the `images` array.
+static void HandlePartitionExists(const std::vector<std::string>& options) {
+    const std::string& partition_name = options[0];
+    std::string has_slot;
+    if (!fb_getvar("has-slot:" + partition_name, &has_slot) ||
+        (has_slot != "yes" && has_slot != "no")) {
+        die("device doesn't have required partition %s!", partition_name.c_str());
+    }
+    bool known_partition = false;
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        if (images[i].nickname && images[i].nickname == partition_name) {
+            images[i].optional_if_no_image = false;
+            known_partition = true;
         }
     }
-    if (fb_execute_queue()) die("requirements not met!");
+    if (!known_partition) {
+        die("device requires partition %s which is not known to this version of fastboot",
+            partition_name.c_str());
+    }
 }
 
-static void queue_info_dump() {
-    fb_queue_notice("--------------------------------------------");
-    fb_queue_display("Bootloader Version...", "version-bootloader");
-    fb_queue_display("Baseband Version.....", "version-baseband");
-    fb_queue_display("Serial Number........", "serialno");
-    fb_queue_notice("--------------------------------------------");
+static void CheckRequirements(const std::string& data) {
+    std::string cur_product;
+    if (!fb_getvar("product", &cur_product)) {
+        fprintf(stderr, "getvar:product FAILED (%s)\n", fb_get_error().c_str());
+    }
+
+    auto lines = Split(data, "\n");
+    for (const auto& line : lines) {
+        if (line.empty()) {
+            continue;
+        }
+
+        std::string name;
+        std::string product;
+        bool invert;
+        std::vector<std::string> options;
+
+        if (!ParseRequirementLine(line, &name, &product, &invert, &options)) {
+            fprintf(stderr, "android-info.txt syntax error: %s\n", line.c_str());
+            continue;
+        }
+        if (name == "partition-exists") {
+            HandlePartitionExists(options);
+        } else {
+            CheckRequirement(cur_product, name, product, invert, options);
+        }
+    }
+}
+
+static void dump_info() {
+    fb_notice("--------------------------------------------");
+    fb_display("Bootloader Version...", "version-bootloader");
+    fb_display("Baseband Version.....", "version-baseband");
+    fb_display("Serial Number........", "serialno");
+    fb_notice("--------------------------------------------");
 }
 
 static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
@@ -773,6 +808,13 @@
         return false;
     }
 
+    if (sparse_file* s = sparse_file_import_auto(fd, false, false)) {
+        buf->image_size = sparse_file_len(s, false, false);
+        sparse_file_destroy(s);
+    } else {
+        buf->image_size = sz;
+    }
+
     lseek64(fd, 0, SEEK_SET);
     int64_t limit = get_sparse_limit(sz);
     if (limit) {
@@ -868,12 +910,12 @@
 
             for (size_t i = 0; i < sparse_files.size(); ++i) {
                 const auto& pair = sparse_files[i];
-                fb_queue_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size());
+                fb_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size());
             }
             break;
         }
         case FB_BUFFER_FD:
-            fb_queue_flash_fd(partition, buf->fd, buf->sz);
+            fb_flash_fd(partition, buf->fd, buf->sz);
             break;
         default:
             die("unknown buffer type: %d", buf->type);
@@ -1021,14 +1063,6 @@
     flash_buf(pname, &buf);
 }
 
-static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
-    int64_t sz;
-    void* data = unzip_to_memory(zip, filename, &sz);
-    if (data == nullptr) return;
-    fb_queue_download("signature", data, sz);
-    fb_queue_command("signature", "installing signature");
-}
-
 // Sets slot_override as the active slot. If slot_override is blank,
 // set current slot as active instead. This clears slot-unbootable.
 static void set_active(const std::string& slot_override) {
@@ -1044,6 +1078,11 @@
     }
 }
 
+static bool is_userspace_fastboot() {
+    std::string value;
+    return fb_getvar("is-userspace", &value) && value == "yes";
+}
+
 static bool if_partition_exists(const std::string& partition, const std::string& slot) {
     std::string has_slot;
     std::string partition_name = partition;
@@ -1063,10 +1102,205 @@
     return fb_getvar("partition-size:" + partition_name, &partition_size);
 }
 
-static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
-    queue_info_dump();
+static bool is_logical(const std::string& partition) {
+    std::string value;
+    return fb_getvar("is-logical:" + partition, &value) && value == "yes";
+}
 
-    fb_queue_query_save("product", cur_product, sizeof(cur_product));
+static void reboot_to_userspace_fastboot() {
+    if (!fb_reboot_to_userspace()) {
+        die("Must reboot to userspace fastboot to flash logical partitions");
+    }
+
+    // Give the current connection time to close.
+    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
+    fb_reinit(open_device());
+}
+
+class ImageSource {
+  public:
+    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
+    virtual int OpenFile(const std::string& name) const = 0;
+};
+
+class FlashAllTool {
+  public:
+    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe);
+
+    void Flash();
+
+  private:
+    void CheckRequirements();
+    void DetermineSecondarySlot();
+    void CollectImages();
+    void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
+    void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
+    void UpdateSuperPartition();
+
+    const ImageSource& source_;
+    std::string slot_override_;
+    bool skip_secondary_;
+    bool wipe_;
+    std::string secondary_slot_;
+    std::vector<std::pair<const Image*, std::string>> boot_images_;
+    std::vector<std::pair<const Image*, std::string>> os_images_;
+};
+
+FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe)
+   : source_(source),
+     slot_override_(slot_override),
+     skip_secondary_(skip_secondary),
+     wipe_(wipe)
+{
+}
+
+void FlashAllTool::Flash() {
+    CheckRequirements();
+    DetermineSecondarySlot();
+    CollectImages();
+
+    // First flash boot partitions. We allow this to happen either in userspace
+    // or in bootloader fastboot.
+    FlashImages(boot_images_);
+
+    // Sync the super partition. This will reboot to userspace fastboot if needed.
+    UpdateSuperPartition();
+
+    // Resize any logical partition to 0, so each partition is reset to 0
+    // extents, and will achieve more optimal allocation.
+    for (const auto& [image, slot] : os_images_) {
+        auto resize_partition = [](const std::string& partition) -> void {
+            if (is_logical(partition)) {
+                fb_resize_partition(partition, "0");
+            }
+        };
+        do_for_partitions(image->part_name, slot, resize_partition, false);
+    }
+
+    // Flash OS images, resizing logical partitions as needed.
+    FlashImages(os_images_);
+
+    if (slot_override_ == "all") {
+        set_active("a");
+    } else {
+        set_active(slot_override_);
+    }
+}
+
+void FlashAllTool::CheckRequirements() {
+    std::vector<char> contents;
+    if (!source_.ReadFile("android-info.txt", &contents)) {
+        die("could not read android-info.txt");
+    }
+    ::CheckRequirements({contents.data(), contents.size()});
+}
+
+void FlashAllTool::DetermineSecondarySlot() {
+    if (skip_secondary_) {
+        return;
+    }
+    if (slot_override_ != "") {
+        secondary_slot_ = get_other_slot(slot_override_);
+    } else {
+        secondary_slot_ = get_other_slot();
+    }
+    if (secondary_slot_ == "") {
+        if (supports_AB()) {
+            fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
+        }
+        skip_secondary_ = true;
+    }
+}
+
+void FlashAllTool::CollectImages() {
+    for (size_t i = 0; i < arraysize(images); ++i) {
+        std::string slot = slot_override_;
+        if (images[i].IsSecondary()) {
+            if (skip_secondary_) {
+                continue;
+            }
+            slot = secondary_slot_;
+        }
+        if (images[i].type == ImageType::BootCritical) {
+            boot_images_.emplace_back(&images[i], slot);
+        } else if (images[i].type == ImageType::Normal) {
+            os_images_.emplace_back(&images[i], slot);
+        }
+    }
+}
+
+void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
+    for (const auto& [image, slot] : images) {
+        fastboot_buffer buf;
+        int fd = source_.OpenFile(image->img_name);
+        if (fd < 0 || !load_buf_fd(fd, &buf)) {
+            if (image->optional_if_no_image) {
+                continue;
+            }
+            die("could not load '%s': %s", image->img_name, strerror(errno));
+        }
+        FlashImage(*image, slot, &buf);
+    }
+}
+
+void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
+    auto flash = [&, this](const std::string& partition_name) {
+        std::vector<char> signature_data;
+        if (source_.ReadFile(image.sig_name, &signature_data)) {
+            fb_download("signature", signature_data);
+            fb_command("signature", "installing signature");
+        }
+
+        if (is_logical(partition_name)) {
+            fb_resize_partition(partition_name, std::to_string(buf->image_size));
+        }
+        flash_buf(partition_name.c_str(), buf);
+    };
+    do_for_partitions(image.part_name, slot, flash, false);
+}
+
+void FlashAllTool::UpdateSuperPartition() {
+    if (!if_partition_exists("super", "")) {
+        return;
+    }
+
+    int fd = source_.OpenFile("super_empty.img");
+    if (fd < 0) {
+        return;
+    }
+    if (!is_userspace_fastboot()) {
+        reboot_to_userspace_fastboot();
+    }
+    fb_download_fd("super", fd, get_file_size(fd));
+
+    std::string command = "update-super:super";
+    if (wipe_) {
+        command += ":wipe";
+    }
+    fb_command(command, "Updating super partition");
+}
+
+class ZipImageSource final : public ImageSource {
+  public:
+    explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+    int OpenFile(const std::string& name) const override;
+
+  private:
+    ZipArchiveHandle zip_;
+};
+
+bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
+    return UnzipToMemory(zip_, name, out);
+}
+
+int ZipImageSource::OpenFile(const std::string& name) const {
+    return unzip_to_file(zip_, name.c_str());
+}
+
+static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
+    dump_info();
 
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
@@ -1074,150 +1308,37 @@
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
 
-    int64_t sz;
-    void* data = unzip_to_memory(zip, "android-info.txt", &sz);
-    if (data == nullptr) {
-        die("update package '%s' has no android-info.txt", filename);
-    }
-
-    check_requirements(reinterpret_cast<char*>(data), sz);
-
-    std::string secondary;
-    if (!skip_secondary) {
-        if (slot_override != "") {
-            secondary = get_other_slot(slot_override);
-        } else {
-            secondary = get_other_slot();
-        }
-        if (secondary == "") {
-            if (supports_AB()) {
-                fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
-            }
-            skip_secondary = true;
-        }
-    }
-    for (size_t i = 0; i < arraysize(images); ++i) {
-        const char* slot = slot_override.c_str();
-        if (images[i].IsSecondary()) {
-            if (!skip_secondary) {
-                slot = secondary.c_str();
-            } else {
-                continue;
-            }
-        }
-
-        int fd = unzip_to_file(zip, images[i].img_name);
-        if (fd == -1) {
-            if (images[i].optional_if_no_image) {
-                continue; // An optional file is missing, so ignore it.
-            }
-            die("non-optional file %s missing", images[i].img_name);
-        }
-
-        if (images[i].optional_if_no_partition &&
-            !if_partition_exists(images[i].part_name, slot)) {
-            continue;
-        }
-
-        fastboot_buffer buf;
-        if (!load_buf_fd(fd, &buf)) {
-            die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
-        }
-
-        auto update = [&](const std::string& partition) {
-            do_update_signature(zip, images[i].sig_name);
-            flash_buf(partition.c_str(), &buf);
-            /* not closing the fd here since the sparse code keeps the fd around
-             * but hasn't mmaped data yet. The temporary file will get cleaned up when the
-             * program exits.
-             */
-        };
-        do_for_partitions(images[i].part_name, slot, update, false);
-    }
-
-    if (slot_override == "all") {
-        set_active("a");
-    } else {
-        set_active(slot_override);
-    }
+    FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false);
+    tool.Flash();
 
     CloseArchive(zip);
 }
 
-static void do_send_signature(const std::string& fn) {
-    std::size_t extension_loc = fn.find(".img");
-    if (extension_loc == std::string::npos) return;
+class LocalImageSource final : public ImageSource {
+  public:
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+    int OpenFile(const std::string& name) const override;
+};
 
-    std::string fs_sig = fn.substr(0, extension_loc) + ".sig";
-
-    int64_t sz;
-    void* data = load_file(fs_sig.c_str(), &sz);
-    if (data == nullptr) return;
-
-    fb_queue_download("signature", data, sz);
-    fb_queue_command("signature", "installing signature");
+bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
+    auto path = find_item_given_name(name);
+    if (path.empty()) {
+        return false;
+    }
+    return ReadFileToVector(path, out);
 }
 
-static void do_flashall(const std::string& slot_override, bool skip_secondary) {
+int LocalImageSource::OpenFile(const std::string& name) const {
+    auto path = find_item_given_name(name);
+    return open(path.c_str(), O_RDONLY);
+}
+
+static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
     std::string fname;
-    queue_info_dump();
+    dump_info();
 
-    fb_queue_query_save("product", cur_product, sizeof(cur_product));
-
-    fname = find_item_given_name("android-info.txt");
-    if (fname.empty()) die("cannot find android-info.txt");
-
-    int64_t sz;
-    void* data = load_file(fname.c_str(), &sz);
-    if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
-
-    check_requirements(reinterpret_cast<char*>(data), sz);
-
-    std::string secondary;
-    if (!skip_secondary) {
-        if (slot_override != "") {
-            secondary = get_other_slot(slot_override);
-        } else {
-            secondary = get_other_slot();
-        }
-        if (secondary == "") {
-            if (supports_AB()) {
-                fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
-            }
-            skip_secondary = true;
-        }
-    }
-
-    for (size_t i = 0; i < arraysize(images); i++) {
-        const char* slot = NULL;
-        if (images[i].IsSecondary()) {
-            if (!skip_secondary) slot = secondary.c_str();
-        } else {
-            slot = slot_override.c_str();
-        }
-        if (!slot) continue;
-        fname = find_item_given_name(images[i].img_name);
-        fastboot_buffer buf;
-        if (!load_buf(fname.c_str(), &buf)) {
-            if (images[i].optional_if_no_image) continue;
-            die("could not load '%s': %s", images[i].img_name, strerror(errno));
-        }
-        if (images[i].optional_if_no_partition &&
-            !if_partition_exists(images[i].part_name, slot)) {
-            continue;
-        }
-        auto flashall = [&](const std::string &partition) {
-            do_send_signature(fname.c_str());
-            flash_buf(partition.c_str(), &buf);
-        };
-        do_for_partitions(images[i].part_name, slot, flashall, false);
-    }
-
-    if (slot_override == "all") {
-        set_active("a");
-    } else {
-        set_active(slot_override);
-    }
+    FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
+    tool.Flash();
 }
 
 static std::string next_arg(std::vector<std::string>* args) {
@@ -1234,7 +1355,7 @@
     while (!args->empty()) {
         command += " " + next_arg(args);
     }
-    fb_queue_command(command, "");
+    fb_command(command, "");
 }
 
 static std::string fb_fix_numeric_var(std::string var) {
@@ -1369,8 +1490,6 @@
     bool wants_set_active = false;
     bool skip_secondary = false;
     bool set_fbe_marker = false;
-    void *data;
-    int64_t sz;
     int longindex;
     std::string slot_override;
     std::string next_active;
@@ -1446,7 +1565,7 @@
                 setvbuf(stdout, nullptr, _IONBF, 0);
                 setvbuf(stderr, nullptr, _IONBF, 0);
             } else if (name == "version") {
-                fprintf(stdout, "fastboot version %s\n", FASTBOOT_VERSION);
+                fprintf(stdout, "fastboot version %s-%s\n", PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str());
                 fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
                 return 0;
 #if !defined(_WIN32)
@@ -1537,7 +1656,7 @@
 
         if (command == "getvar") {
             std::string variable = next_arg(&args);
-            fb_queue_display(variable, variable);
+            fb_display(variable, variable);
         } else if (command == "erase") {
             std::string partition = next_arg(&args);
             auto erase = [&](const std::string& partition) {
@@ -1549,7 +1668,7 @@
                             partition_type.c_str());
                 }
 
-                fb_queue_erase(partition);
+                fb_erase(partition);
             };
             do_for_partitions(partition, slot_override, erase, true);
         } else if (android::base::StartsWith(command, "format")) {
@@ -1574,11 +1693,13 @@
             do_for_partitions(partition.c_str(), slot_override, format, true);
         } else if (command == "signature") {
             std::string filename = next_arg(&args);
-            data = load_file(filename.c_str(), &sz);
-            if (data == nullptr) die("could not load '%s': %s", filename.c_str(), strerror(errno));
-            if (sz != 256) die("signature must be 256 bytes (got %" PRId64 ")", sz);
-            fb_queue_download("signature", data, sz);
-            fb_queue_command("signature", "installing signature");
+            std::vector<char> data;
+            if (!ReadFileToVector(filename, &data)) {
+                die("could not load '%s': %s", filename.c_str(), strerror(errno));
+            }
+            if (data.size() != 256) die("signature must be 256 bytes (got %zu)", data.size());
+            fb_download("signature", data);
+            fb_command("signature", "installing signature");
         } else if (command == "reboot") {
             wants_reboot = true;
 
@@ -1606,7 +1727,7 @@
         } else if (command == "reboot-fastboot") {
             wants_reboot_fastboot = true;
         } else if (command == "continue") {
-            fb_queue_command("continue", "resuming boot");
+            fb_command("continue", "resuming boot");
         } else if (command == "boot") {
             std::string kernel = next_arg(&args);
             std::string ramdisk;
@@ -1614,9 +1735,9 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
-            fb_queue_download("boot.img", data, sz);
-            fb_queue_command("boot", "booting");
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            fb_download("boot.img", data);
+            fb_command("boot", "booting");
         } else if (command == "flash") {
             std::string pname = next_arg(&args);
 
@@ -1640,17 +1761,17 @@
             std::string second_stage;
             if (!args.empty()) second_stage = next_arg(&args);
 
-            data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
-            auto flashraw = [&](const std::string& partition) {
-                fb_queue_flash(partition, data, sz);
+            auto data = LoadBootableImage(kernel, ramdisk, second_stage);
+            auto flashraw = [&data](const std::string& partition) {
+                fb_flash(partition, data);
             };
             do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
             if (slot_override == "all") {
                 fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
-                do_flashall(slot_override, true);
+                do_flashall(slot_override, true, wants_wipe);
             } else {
-                do_flashall(slot_override, skip_secondary);
+                do_flashall(slot_override, skip_secondary, wants_wipe);
             }
             wants_reboot = true;
         } else if (command == "update") {
@@ -1674,10 +1795,10 @@
             if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
-            fb_queue_download_fd(filename, buf.fd, buf.sz);
+            fb_download_fd(filename, buf.fd, buf.sz);
         } else if (command == "get_staged") {
             std::string filename = next_arg(&args);
-            fb_queue_upload(filename);
+            fb_upload(filename);
         } else if (command == "oem") {
             do_oem_command("oem", &args);
         } else if (command == "flashing") {
@@ -1694,14 +1815,14 @@
         } else if (command == "create-logical-partition") {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
-            fb_queue_create_partition(partition, size);
+            fb_create_partition(partition, size);
         } else if (command == "delete-logical-partition") {
             std::string partition = next_arg(&args);
-            fb_queue_delete_partition(partition);
+            fb_delete_partition(partition);
         } else if (command == "resize-logical-partition") {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
-            fb_queue_resize_partition(partition, size);
+            fb_resize_partition(partition, size);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
@@ -1713,7 +1834,7 @@
             std::string partition_type;
             if (!fb_getvar(std::string{"partition-type:"} + partition, &partition_type)) continue;
             if (partition_type.empty()) continue;
-            fb_queue_erase(partition);
+            fb_erase(partition);
             if (partition == "userdata" && set_fbe_marker) {
                 fprintf(stderr, "setting FBE marker on initial userdata...\n");
                 std::string initial_userdata_dir = create_fbemarker_tmpdir();
@@ -1728,22 +1849,25 @@
         fb_set_active(next_active);
     }
     if (wants_reboot && !skip_reboot) {
-        fb_queue_reboot();
-        fb_queue_wait_for_disconnect();
+        fb_reboot();
+        fb_wait_for_disconnect();
     } else if (wants_reboot_bootloader) {
-        fb_queue_command("reboot-bootloader", "rebooting into bootloader");
-        fb_queue_wait_for_disconnect();
+        fb_command("reboot-bootloader", "rebooting into bootloader");
+        fb_wait_for_disconnect();
     } else if (wants_reboot_recovery) {
-        fb_queue_command("reboot-recovery", "rebooting into recovery");
-        fb_queue_wait_for_disconnect();
+        fb_command("reboot-recovery", "rebooting into recovery");
+        fb_wait_for_disconnect();
     } else if (wants_reboot_fastboot) {
-        fb_queue_command("reboot-fastboot", "rebooting into fastboot");
-        fb_queue_wait_for_disconnect();
+        fb_command("reboot-fastboot", "rebooting into fastboot");
+        fb_wait_for_disconnect();
     }
 
-    int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
-    return status;
+
+    if (Transport* old_transport = fb.set_transport(nullptr)) {
+        delete old_transport;
+    }
+    return 0;
 }
 
 void FastBootTool::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 55ca65d..72ba619 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -52,11 +52,14 @@
 /*************************** PUBLIC *******************************/
 FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
                                bool no_checks)
-    : transport(transport) {
+    : transport_(transport) {
     info_cb_ = info;
     disable_checks_ = no_checks;
 }
 
+FastBootDriver::~FastBootDriver() {
+}
+
 RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
     return RawCommand(Commands::BOOT, response, info);
 }
@@ -85,22 +88,18 @@
     return GetVar("all", &tmp, response);
 }
 
-RetCode FastBootDriver::Powerdown(std::string* response, std::vector<std::string>* info) {
-    return RawCommand(Commands::POWERDOWN, response, info);
-}
-
 RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
     return RawCommand(Commands::REBOOT, response, info);
 }
 
-RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
-                                  std::vector<std::string>* info) {
-    return RawCommand(Commands::SET_ACTIVE + part, response, info);
+RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
+                                 std::vector<std::string>* info) {
+    return RawCommand("reboot-" + target, response, info);
 }
 
-RetCode FastBootDriver::Verify(uint32_t num, std::string* response, std::vector<std::string>* info) {
-    std::string cmd = android::base::StringPrintf("%s%08" PRIx32, Commands::VERIFY.c_str(), num);
-    return RawCommand(cmd, response, info);
+RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
+                                  std::vector<std::string>* info) {
+    return RawCommand(Commands::SET_ACTIVE + slot, response, info);
 }
 
 RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
@@ -127,7 +126,7 @@
     return RawCommand(Commands::FLASH + part);
 }
 
-RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts) {
+RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* parts) {
     std::vector<std::string> all;
     RetCode ret;
     if ((ret = GetVarAll(&all))) {
@@ -141,35 +140,13 @@
         if (std::regex_match(s, sm, reg)) {
             std::string m1(sm[1]);
             std::string m2(sm[2]);
-            uint32_t tmp = strtol(m2.c_str(), 0, 16);
+            uint64_t tmp = strtoll(m2.c_str(), 0, 16);
             parts->push_back(std::make_tuple(m1, tmp));
         }
     }
     return SUCCESS;
 }
 
-RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
-                                bool* reqmet, bool invert) {
-    *reqmet = invert;
-    RetCode ret;
-    std::string response;
-    if ((ret = GetVar(var, &response))) {
-        return ret;
-    }
-
-    // Now check if we have a match
-    for (const auto s : allowed) {
-        // If it ends in *, and starting substring match
-        if (response == s || (s.length() && s.back() == '*' &&
-                              !response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
-            *reqmet = !invert;
-            break;
-        }
-    }
-
-    return SUCCESS;
-}
-
 RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
                                  std::vector<std::string>* info) {
     RetCode ret;
@@ -195,24 +172,19 @@
 
 RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
                                  std::vector<std::string>* info) {
-    return Download(buf.data(), buf.size(), response, info);
-}
-
-RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
-                                 std::vector<std::string>* info) {
     RetCode ret;
     error_ = "";
-    if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
+    if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
         error_ = "Buffer is too large or 0 bytes";
         return BAD_ARG;
     }
 
-    if ((ret = DownloadCommand(size, response, info))) {
+    if ((ret = DownloadCommand(buf.size(), response, info))) {
         return ret;
     }
 
     // Write the buffer
-    if ((ret = SendBuffer(buf, size))) {
+    if ((ret = SendBuffer(buf))) {
         return ret;
     }
 
@@ -220,10 +192,10 @@
     return HandleResponse(response, info);
 }
 
-RetCode FastBootDriver::Download(sparse_file* s, std::string* response,
+RetCode FastBootDriver::Download(sparse_file* s, bool use_crc, std::string* response,
                                  std::vector<std::string>* info) {
     error_ = "";
-    int64_t size = sparse_file_len(s, true, false);
+    int64_t size = sparse_file_len(s, true, use_crc);
     if (size <= 0 || size > MAX_DOWNLOAD_SIZE) {
         error_ = "Sparse file is too large or invalid";
         return BAD_ARG;
@@ -247,7 +219,7 @@
         return data->self->SparseWriteCallback(data->tpbuf, cbuf, len);
     };
 
-    if (sparse_file_callback(s, true, false, cb, &cb_priv) < 0) {
+    if (sparse_file_callback(s, true, use_crc, cb, &cb_priv) < 0) {
         error_ = "Error reading sparse file";
         return IO_ERROR;
     }
@@ -332,7 +304,7 @@
 }
 
 RetCode FastBootDriver::WaitForDisconnect() {
-    return transport->WaitForDisconnect() ? IO_ERROR : SUCCESS;
+    return transport_->WaitForDisconnect() ? IO_ERROR : SUCCESS;
 }
 
 /****************************** PROTECTED *************************************/
@@ -344,7 +316,7 @@
         return BAD_ARG;
     }
 
-    if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
+    if (transport_->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
         error_ = ErrnoStr("Write to device failed");
         return IO_ERROR;
     }
@@ -378,7 +350,7 @@
     // erase response
     set_response("");
     while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
-        int r = transport->Read(status, FB_RESPONSE_SZ);
+        int r = transport_->Read(status, FB_RESPONSE_SZ);
         if (r < 0) {
             error_ = ErrnoStr("Status read failed");
             return IO_ERROR;
@@ -427,11 +399,9 @@
 const std::string FastBootDriver::Commands::ERASE = "erase:";
 const std::string FastBootDriver::Commands::FLASH = "flash:";
 const std::string FastBootDriver::Commands::GET_VAR = "getvar:";
-const std::string FastBootDriver::Commands::POWERDOWN = "powerdown";
 const std::string FastBootDriver::Commands::REBOOT = "reboot";
 const std::string FastBootDriver::Commands::SET_ACTIVE = "set_active:";
 const std::string FastBootDriver::Commands::UPLOAD = "upload";
-const std::string FastBootDriver::Commands::VERIFY = "verify:";
 
 /******************************* PRIVATE **************************************/
 RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
@@ -472,7 +442,7 @@
         return BAD_ARG;
     }
     // Write the buffer
-    ssize_t tmp = transport->Write(buf, size);
+    ssize_t tmp = transport_->Write(buf, size);
 
     if (tmp < 0) {
         error_ = ErrnoStr("Write to device failed in SendBuffer()");
@@ -493,7 +463,7 @@
 
 RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
     // Read the buffer
-    ssize_t tmp = transport->Read(buf, size);
+    ssize_t tmp = transport_->Read(buf, size);
 
     if (tmp < 0) {
         error_ = ErrnoStr("Read from device failed in ReadBuffer()");
@@ -539,4 +509,9 @@
     return 0;
 }
 
+Transport* FastBootDriver::set_transport(Transport* transport) {
+    std::swap(transport_, transport);
+    return transport;
+}
+
 }  // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index dd199c0..2d45085 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -56,6 +56,8 @@
 };
 
 class FastBootDriver {
+    friend class FastBootTest;
+
   public:
     static constexpr int RESP_TIMEOUT = 30;  // 30 seconds
     static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
@@ -64,6 +66,7 @@
     FastBootDriver(Transport* transport,
                    std::function<void(std::string&)> info = [](std::string&) {},
                    bool no_checks = false);
+    ~FastBootDriver();
 
     RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
@@ -71,10 +74,7 @@
                      std::vector<std::string>* info = nullptr);
     RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
-    // This will be removed after fastboot is modified to use a vector
-    RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
-                     std::vector<std::string>* info = nullptr);
-    RetCode Download(sparse_file* s, std::string* response = nullptr,
+    RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
     RetCode Erase(const std::string& part, std::string* response = nullptr,
                   std::vector<std::string>* info = nullptr);
@@ -83,21 +83,20 @@
     RetCode GetVar(const std::string& key, std::string* val,
                    std::vector<std::string>* info = nullptr);
     RetCode GetVarAll(std::vector<std::string>* response);
-    RetCode Powerdown(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
-    RetCode SetActive(const std::string& part, std::string* response = nullptr,
+    RetCode RebootTo(std::string target, std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr);
+    RetCode SetActive(const std::string& slot, std::string* response = nullptr,
                       std::vector<std::string>* info = nullptr);
     RetCode Upload(const std::string& outfile, std::string* response = nullptr,
                    std::vector<std::string>* info = nullptr);
-    RetCode Verify(uint32_t num, std::string* response = nullptr,
-                   std::vector<std::string>* info = nullptr);
 
     /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
     RetCode FlashPartition(const std::string& part, const std::vector<char>& data);
     RetCode FlashPartition(const std::string& part, int fd, uint32_t sz);
     RetCode FlashPartition(const std::string& part, sparse_file* s);
 
-    RetCode Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts);
+    RetCode Partitions(std::vector<std::tuple<std::string, uint64_t>>* parts);
     RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,
                     bool invert = false);
 
@@ -107,6 +106,10 @@
     std::string Error();
     RetCode WaitForDisconnect();
 
+    // Note: set_transport will return the previous transport.
+    Transport* set_transport(Transport* transport);
+    Transport* transport() const { return transport_; }
+
     // This is temporarily public for engine.cpp
     RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
                        std::vector<std::string>* info = nullptr, int* dsize = nullptr);
@@ -127,14 +130,12 @@
         static const std::string ERASE;
         static const std::string FLASH;
         static const std::string GET_VAR;
-        static const std::string POWERDOWN;
         static const std::string REBOOT;
         static const std::string SET_ACTIVE;
         static const std::string UPLOAD;
-        static const std::string VERIFY;
     };
 
-    Transport* const transport;
+    Transport* transport_;
 
   private:
     RetCode SendBuffer(int fd, size_t size);
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 43201fa..e0bbd56 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -59,3 +59,145 @@
     EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.128.3"), "bad OS version");
     EXPECT_DEATH(fb.ParseOsVersion(&hdr, "1.2.128"), "bad OS version");
 }
+
+extern bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,
+                                 bool* invert, std::vector<std::string>* options);
+
+static void ParseRequirementLineTest(const std::string& line, const std::string& expected_name,
+                                     const std::string& expected_product, bool expected_invert,
+                                     const std::vector<std::string>& expected_options) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_TRUE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+
+    EXPECT_EQ(expected_name, name) << line;
+    EXPECT_EQ(expected_product, product) << line;
+    EXPECT_EQ(expected_invert, invert) << line;
+    EXPECT_EQ(expected_options, options) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineSuccesses) {
+    // Examples provided in the code + slight variations.
+    ParseRequirementLineTest("require product=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require version-bootloader=1234", "version-bootloader", "", false,
+                             {"1234"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+    ParseRequirementLineTest("require-for-product:gamma version-bootloader=istanbul|constantinople",
+                             "version-bootloader", "gamma", false, {"istanbul", "constantinople"});
+    ParseRequirementLineTest("require partition-exists=vendor", "partition-exists", "", false,
+                             {"vendor"});
+    ParseRequirementLineTest("reject product=alpha", "product", "", true, {"alpha"});
+    ParseRequirementLineTest("reject product=alpha|beta|gamma", "product", "", true,
+                             {"alpha", "beta", "gamma"});
+
+    // Without any prefix, assume 'require'
+    ParseRequirementLineTest("product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    // Including if the variable name is otherwise a prefix keyword
+    ParseRequirementLineTest("require = alpha", "require", "", false, {"alpha"});
+    ParseRequirementLineTest("reject = alpha", "reject", "", false, {"alpha"});
+    ParseRequirementLineTest("require-for-product:gamma = alpha", "require-for-product:gamma", "",
+                             false, {"alpha"});
+
+    // Extra spaces are allowed.
+    ParseRequirementLineTest("require    product=alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product    =alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product   =   alpha|beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha  |  beta|gamma", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("product  =  alpha  |  beta  |  gamma   ", "product", "", false,
+                             {"alpha", "beta", "gamma"});
+    ParseRequirementLineTest("require-for-product:  gamma version-bootloader=istanbul",
+                             "version-bootloader", "gamma", false, {"istanbul"});
+
+    // Extraneous ending | is okay, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha|", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta|gamma|", "product", "", false,
+                             {"alpha", "beta", "gamma", ""});
+
+    // Accept empty options, double ||, etc, implies accepting an empty string.
+    ParseRequirementLineTest("require product=alpha||beta|   |gamma", "product", "", false,
+                             {"alpha", "", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||beta|gamma", "product", "", false,
+                             {"alpha", "", "beta", "gamma"});
+    ParseRequirementLineTest("require product=alpha|beta|   |gamma", "product", "", false,
+                             {"alpha", "beta", "", "gamma"});
+    ParseRequirementLineTest("require product=alpha||", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha|| ", "product", "", false, {"alpha", "", ""});
+    ParseRequirementLineTest("require product=alpha| ", "product", "", false, {"alpha", ""});
+    ParseRequirementLineTest("require product=alpha|beta| ", "product", "", false,
+                             {"alpha", "beta", ""});
+
+    // No option string is also treating as accepting an empty string.
+    ParseRequirementLineTest("require =", "require", "", false, {""});
+    ParseRequirementLineTest("require = |", "require", "", false, {"", ""});
+    ParseRequirementLineTest("reject =", "reject", "", false, {""});
+    ParseRequirementLineTest("reject = |", "reject", "", false, {"", ""});
+    ParseRequirementLineTest("require-for-product: =", "require-for-product:", "", false, {""});
+    ParseRequirementLineTest("require-for-product: = | ", "require-for-product:", "", false,
+                             {"", ""});
+    ParseRequirementLineTest("require product=", "product", "", false, {""});
+    ParseRequirementLineTest("require product = ", "product", "", false, {""});
+    ParseRequirementLineTest("require product = | ", "product", "", false, {"", ""});
+    ParseRequirementLineTest("reject product=", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = ", "product", "", true, {""});
+    ParseRequirementLineTest("reject product = | ", "product", "", true, {"", ""});
+    ParseRequirementLineTest("require-for-product:gamma product=", "product", "gamma", false, {""});
+    ParseRequirementLineTest("require-for-product:gamma product = ", "product", "gamma", false,
+                             {""});
+    ParseRequirementLineTest("require-for-product:gamma product = |", "product", "gamma", false,
+                             {"", ""});
+
+    // Check for board -> product substitution.
+    ParseRequirementLineTest("require board=alpha", "product", "", false, {"alpha"});
+    ParseRequirementLineTest("board=alpha", "product", "", false, {"alpha"});
+}
+
+static void ParseRequirementLineTestMalformed(const std::string& line) {
+    std::string name;
+    std::string product;
+    bool invert;
+    std::vector<std::string> options;
+
+    EXPECT_FALSE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;
+}
+
+TEST(FastBoot, ParseRequirementLineMalformed) {
+    ParseRequirementLineTestMalformed("nothing");
+    ParseRequirementLineTestMalformed("");
+    ParseRequirementLineTestMalformed("=");
+    ParseRequirementLineTestMalformed("|");
+
+    ParseRequirementLineTestMalformed("require");
+    ParseRequirementLineTestMalformed("require ");
+    ParseRequirementLineTestMalformed("reject");
+    ParseRequirementLineTestMalformed("reject ");
+    ParseRequirementLineTestMalformed("require-for-product:");
+    ParseRequirementLineTestMalformed("require-for-product: ");
+
+    ParseRequirementLineTestMalformed("require product");
+    ParseRequirementLineTestMalformed("reject product");
+
+    ParseRequirementLineTestMalformed("require-for-product:gamma");
+    ParseRequirementLineTestMalformed("require-for-product:gamma product");
+
+    // No spaces allowed before between require-for-product and :.
+    ParseRequirementLineTestMalformed("require-for-product :");
+}
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index b0ac2ef..fc6a16b 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -8,7 +8,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
 #include <sys/wait.h>
 #else
 #include <tchar.h>
@@ -27,7 +27,7 @@
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
-#ifdef WIN32
+#ifdef _WIN32
 static int exec_cmd(const char* path, const char** argv, const char** envp) {
     std::string cmd;
     int i = 0;
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
new file mode 100644
index 0000000..301534b
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -0,0 +1,41 @@
+cc_test_host {
+  name: "fuzzy_fastboot",
+  compile_multilib: "first",
+
+  srcs: [
+    "main.cpp",
+    "extensions.cpp",
+    "usb_transport_sniffer.cpp",
+    "fixtures.cpp",
+    "test_utils.cpp",
+  ],
+
+  static_libs: [
+    "libfastboot2",
+    "libziparchive",
+    "libsparse",
+    "libutils",
+    "liblog",
+    "libz",
+    "libdiagnose_usb",
+    "libbase",
+    "libcutils",
+    "libgtest",
+    "libgtest_main",
+    "libbase",
+    "libadb_host",
+    "libtinyxml2",
+    "libsparse",
+  ],
+
+  // Static libs (libfastboot2) shared library dependencies are not transitively included
+  // This is needed to avoid link time errors when building for mac
+  target: {
+    darwin: {
+      host_ldlibs: [
+          "-framework CoreFoundation",
+          "-framework IOKit",
+      ],
+    },
+  }
+}
diff --git a/fastboot/fuzzy_fastboot/README.md b/fastboot/fuzzy_fastboot/README.md
new file mode 100644
index 0000000..72967c5
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/README.md
@@ -0,0 +1,394 @@
+# Fuzzy Fastboot
+
+Fuzzy Fastboot (FF) is a standalone automated conformance and penetration tester for
+validating device-side fastboot protocol implementations.
+The tool is completely generic, and uses a simple extensible XML
+configuration file to auto-generate device-specific tests for any device.
+Any Android device that uses the fastboot protocol should have fuzzy fastboot run on it prior to
+release to find implementation bugs, make sure it conforms to the fastboot spec,
+and that it safely handles malicious inputs.
+
+
+## Background
+The [fastboot protocol](../README.md) provides an easy way to manage low level
+aspects of the device directly from bootloader. However, with great power comes
+great responsibility. An improper or insecure fastboot implementation can
+open the possibility for critical security exploits on the bootloader via fastboot
+commands. Furthermore, an untrustworthy or insecure bootloader means nothing that is
+either directly or indirectly bootstrapped by the bootloader can be trusted (including Android).
+By checking a bootloader's conformance to the fastboot spec, as well as make sure
+nefarious/malformed input is properly and gracefully handled, easy exploits of a
+device's bootloaders can be mitigated.
+
+Additionally, since the fastboot tool itself must support a myriad of fastboot
+implementations, it is important to make sure an implementation is conforming to
+avoid potential incompatibilities with the fastboot command line tool itself.
+Thus, Fuzzy Fastboot also checks for proper conformance to the spec.
+
+## Overview
+Fuzzy Fastboot is written in C++ and uses [Google Test](https://github.com/google/googletest)
+for the underlying test framework. This means that Fuzzy Fastboot supports all of
+gtest's command line flags and options.
+
+Additionally, by using gtest it makes it extremely easy to add additional C++ based
+tests to Fuzzy Fastboot. However, in most cases the optional device specific
+XML configuration file that is provided to Fuzzy Fastboot supports the necessary
+features and hooks for testing device specific commands/features
+without touching the underlying C++.
+
+### Generic Tests
+Without a provided device XML configuration, Fuzzy Fastboot can only perform
+some basic tests that are generic to any fastboot device. These generic tests are
+divided into several test suite categories:
+
+1. **USBFunctionality** - Test USB communication
+2. **Conformance** - Test the device properly handles well-formed fastboot commands
+3. **UnlockPermissions** - Test commands only allowed in the unlocked state work
+4. **LockPermissions** - Test commands only not allowed in the locked state are rejected
+5. **Fuzz** - Test malicious and/or ill-formed commands are properly and gracefully handled
+
+
+### XML Generated Tests
+With a provided XML device configuration, Fuzzy Fastboot will be able to generate
+many more additional tests cases.
+
+The device config XML has five element pairs all inside a root level `<config>`:
+
+#### `<getvar>` Element
+Inside the `<getvar></getvar>` element pairs, one should list all the device's getvar
+variables, with an associated ECMAScript regex you wish the returned variable to match on.
+Each tested variable should appear in a `<var key="key" assert="regex"/>` format.
+For example:
+```xml
+<getvar>
+  <var key="product" assert="superphone2000"/>
+  <var key="secure" assert="no|yes"/>
+  <var key="battery-voltage" assert="[34][[:digit:]]{3}"/>
+  <!-- etc...  -->
+</getvar>
+```
+
+#### `<partitions>` Element
+Inside the `<partitions></partitions>` element pairs, one should list all the device's
+partitions. Each device partition has should be put inside a `<part/>` element.
+The `<part/>` element supports the following attributes:
+
+
+| Attribute | Value          | Purpose                                                                                     | Default  |
+|-----------|----------------|---------------------------------------------------------------------------------------------|----------|
+| value     | Partition name | The name of the partition                                                                   | Required |
+| slots     | "yes" or "no"  | Is this partition is slotted                                                                | "no"     |
+| test      | "yes" or "no"  | Is Fuzzy Fastboot is allowed to generate tests that overwrite this partition                | Required |
+| hashable  | "yes" or "no"  | Is this partition hashable with the hash command specified in `<checksum>`                  | "yes"    |
+| parsed    | "yes" or "no"  | Does the bootloader parse this partition, such as look for a header, look for magic, etc... | "no"     |
+
+For example:
+```xml
+<!-- All the device partitions should be listed here -->
+<partitions>
+  <part value="boot" slots="yes" test="yes" hashable="yes" parsed="yes"/>
+  <part value="modem" slots="yes" test="yes" hashable="yes"/>
+  <part value="userdata" slots="no" test="yes" hashable="no"/>
+  <!-- etc...  -->
+</partitions>
+```
+
+#### `<packed>` Element
+Most devices have pseudo partitions, such as a `bootloader` partition,
+that in reality is composed of several real partitions.
+When one of these pseudo partitions is flashed, the bootloader
+will internally expand the image into the individual images for each underlying
+partition. These pseudo partitions should be listed inside a `<part></part>`
+element pair. Each element `<part>` has a mandatory attribute `value`,
+which lists the name of this pseudo partition, and a `slots` attribute,
+which can be yes or no if this pseudo partition is slotted.
+Additionally, inside the `<part></part>` element pair, one should list
+all the real partition that make up this pseudo partition inside of
+`<child>PART_NAME</child>` element pairs.
+An example is should below:
+
+```xml
+<!-- All the device packed partitions should be listed here -->
+<packed>
+	<part value="bootloader" slots="yes">
+		<!-- We list the real partitions it is composed of -->
+		<child>foo1</child>
+		<child>foo2</child>
+		<child>bar3</child>
+		<!-- We list tests, expect defaults to 'okay' -->
+		<test packed="bootloader.img" unpacked="unpacked"/>
+		<test packed="bootloader_garbage.img" expect="fail"/>
+	</part>
+</packed>
+```
+
+You might notice there are additional `<test/>` elements as well contained inside of
+a `<part></part>` pair. This is because Fuzzy Fastboot allows (and recommends) one to specify
+valid and invalid test packed images for flashing this particular pseudo partition.
+Additionally, one should specify a folder with all the partitions' images
+that the packed image unpacks to. If your device supports hashing partitions, this
+will allow Fuzzy Fastboot to validate the images are unpacking correctly, and
+the correct slots are being flashed.
+
+Each `<test/>` element has the following supported attributes:
+
+| Attribute | Value | Purpose | Default |
+|-----------|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------|
+| packed | The name of the packed test image | The image uploaded to the device. It is searched for in dir if --search_path=dir | Required |
+| unpacked | The name of the directory containing the unpacked version of packed | Searched for in dir if --search_path=dir. This folder should have the all the images that packed unpacks to. The name of each of the images should be the name of the real partition it is flashed to. | Required if expect != "fail" |
+| expect | "okay" or "fail" | If uploading a invalid or garbage image the bootloader should reject use "fail" otherwise "okay" | "okay" |
+
+
+#### `<oem>` Element
+Vendors can extend the fastboot protocol with oem commands. This allows vendors
+to support device/vendor specific features/commands over the fastboot protocol.
+Fuzzy Fastboot allows testing these oem commands as well.
+
+Oem commands are specefied in `<command></command>` element pairs. Each command
+element supports the following attributes:
+
+
+| Attribute   | Value                | Purpose                                                       | Default  |
+|-------------|----------------------|---------------------------------------------------------------|----------|
+| value       | The oem command name | Ex: if value="foo", the oem command will start with "oem foo" | Required |
+| permissions | "none" or "unlocked" | Whether the bootloader must be "unlocked" to perform command  | "none"   |
+
+An example is should below:
+```xml
+<oem>
+  <command value="self_destruct" permissions="unlocked">
+    <!-- This will test that "oem self_destruct now" returns 'okay' -->
+    <test value="now" expect="okay"/>
+    <!-- This will test that "oem self_destruct yesterday" returns 'fail' -->
+    <test value="yesterday" expect="fail" />
+  </command>
+
+  <command value="foobar" permissions="unlocked">
+    <!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->
+    <test value="use_staged" expect="okay" input="test_image.img" />
+    <!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->
+    <test value="send_response" expect="fail" validate="python validator.py"/>
+  </command>
+<oem/>
+```
+
+Again you will notice that one can, and should, specify tests to run with `<test/>` elements.
+The test elements support the following attributes:
+
+
+| Attribute | Value                                            | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                            | Default                    |
+|-----------|--------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------|
+| value     | The oem command argument                         | Ex: if value="bar", and the oem command name was "foo", the full command will be "oem foo bar"                                                                                                                                                                                                                                                                                                                                                     | Empty String (no argument) |
+| expect    | "okay" or "fail"                                 | Whether the bootloader should accept or reject this command                                                                                                                                                                                                                                                                                                                                                                                        | "okay"                     |
+| input     | A image filename                                 | Some oem commands require staging files before the command is executed                                                                                                                                                                                                                                                                                                                                                                             | Empty String (no argument) |
+| validate  | A program/script to run to validate the response | Some oem commands will stage data that can be downloaded afterwards and should be validated to be correct. Fuzzy Fastboot will launch the validation program with the first arg the oem command executed, the second arg  the path to the downloaded image. Ex: "python validate.py'. If the program has a non-zero  return code, the validation is marked as failed and anything from the launched programs stderr is logged in the test failure. | Empty String (no argument) |
+| assert    | A Regular expression                             | In the "okay" or "fail" response, Fuzzy Fastboot will assert the  response matches this regular expression.                                                                                                                                                                                                                                                                                                                                        | Empty String (no argument) |
+| output    | The name of the saved file                       | This is the name of the saved output file passed to the validation script. It is saved in whatever location is specified by the --output_path argument                                                                                                                                                                                                                                                                                             | out.img                    |
+
+
+#### `<checksum/>` Element
+If the bootloader supports hashing partitions (implementing this is strongly recommended), Fuzzy Fastboot can
+use it to do a bunch more testing. Make sure this hash is a cryptographically secure hash, as a non-secure one
+might reveal secrets about the partitions' contents.
+
+The checksum element has no children and only two required attributes:
+
+- **value** - The command that hashes a partition. Note that the name of the partition will be appended to the end of the command. For example, if `value="oem hash"`, hashing the partition `bar` would be issued with `oem hash bar`.
+- **parser** - How the hash is returned is up to the vendor's implementation. It could be part of the `OKAY` response, or be encoded in `INFO` responses. Thus, the parser attribute is used to specify a program/script that will extract the hash. The first argument to the program will be the be the response from `OKAY`, the second argument will be all the `INFO` responses joined by newlines. The extracted hash should be sent back to Fuzzy Fastboot as a string written to stderr, and a return code of 0 to signal the parsing was successful. In the case of failure, return a non-zero return code, an optionally an associated error message written to stderr.
+
+
+
+## Full Example XML Configuration
+Here is a basic example configuration. This can also be found in the 'example' folder
+as well as the associated python scripts 'checksum_parser.py' (used to extract partition hash),
+and 'validator.py' (used to validate an oem command that returns data).
+```xml
+<?xml version="1.0"?>
+<config>
+<!-- All the device getvar variables should be listed here -->
+<getvar>
+	<var key="product" assert="superphone2000"/>
+	<var key="secure" assert="no|yes"/>
+</getvar>
+
+<!-- All the device partitions should be listed here -->
+<partitions>
+	<part value="boot" slots="yes" test="yes" hashable="yes" parsed="yes"/>
+	<part value="modem" slots="yes" test="yes" hashable="yes"/>
+	<part value="userdata" slots="no" test="yes" hashable="no"/>
+
+	<!-- Bootloader partitions -->
+	<part value="foo1" slots="yes" test="no" hashable="yes"/>
+	<part value="foo2" slots="yes" test="no" hashable="yes"/>
+	<part value="bar3" slots="yes" test="no" hashable="yes"/>
+</partitions>
+
+<!-- All the device packed partitions should be listed here -->
+<packed>
+	<part value="bootloader" slots="yes">
+		<!-- We list the real partitions it is composed of -->
+		<child>foo1</child>
+		<child>foo2</child>
+		<child>bar3</child>
+		<!-- We list tests, expect defaults to 'okay' -->
+		<test packed="bootloader.img" unpacked="unpacked"/>
+		<test packed="bootloader_garbage.img" expect="fail"/>
+	</part>
+</packed>
+
+<!-- All the oem commands should be listed here -->
+<oem>
+	<!-- The 'oem self_destruct' command requires an unlocked bootloader -->
+	<command value="self_destruct" permissions="unlocked">
+		<!-- This will test that "oem self_destruct now" returns 'okay' -->
+		<test value="now" expect="okay"/>
+		<test value="yesterday" expect="fail" />
+	</command>
+
+	<!-- Test a fictional 'oem get' command -->
+	<command value="get" permissions="none">
+		<test value="batch_id" expect="okay" assert="[[:digit:]]+"/>
+		<test value="device_color" expect="okay" assert="green|blue"/>
+		<test value="build_num" expect="okay" assert="[\w\-.]+"/>
+		<test value="garbage" expect="fail" assert="Invalid var '[\w ]+'"/>
+	</command>
+
+	<!-- Some oem commands might require staging or downloading data, or both -->
+	<command value="foobar" permissions="unlocked">
+		<!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->
+		<test value="use_staged" expect="okay" input="test_image.img" />
+		<!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->
+		<test value="send_response" expect="fail" validate="python validator.py"/>
+	</command>
+</oem>
+
+<!-- If there is a custom oem checksum command to hash partitions, add it here -->
+<checksum value="oem sha1sum"/>
+</config>
+
+```
+
+## Running Fuzzy Fastboot
+Fuzzy Fastboot is built with the fastboot tool itself. It will appear in `out/host/linux-x86/testcases/fuzzy_fastboot/x86_64`.
+
+### Command Line Arguments
+- **--config=**: Specify the name of the configuration XML file. If omitted, only the generic tests will be available.
+- **--search_path=**: Specify the path where Fuzzy Fastboot will look for files referenced in the XML. This includes all the test images and the referenced programs/scripts. This is also where the --config is searched for. If this argument is omitted it defaults to the current directory.
+- **--output_path**: Some oem tests can download an image to the host for validation. This is the location where that image is stored. This deafults to '/tmp'.
+- **--serial_port**: Many devices have a UART or serial log, that reports logging information. Fuzzy Fastboot can include this logging information in the backtraces it generates. This can make debugging far easier. If your device has this, it can be specified with the path to the tty device. Ex: "/dev/ttyUSB0".
+- **--gtest_***: Any valid gtest argument (they all start with 'gtest_')
+- **-h**: Print gtest's help message
+
+
+## Using Fuzzy Fastboot on my Device
+All Fuzzy Fastboot tests should pass on your device. No test should be able to
+crash the bootloader. Invalid input MUST be handled gracefully. Using "asserts"
+or panicking on invalid or malformed input is not an acceptable way to handle
+these tests, as ungraceful forced termination of the bootloader can expose
+vulnerabilities and leave the device in a bad state.
+
+The following is the recommended workflow for using Fuzzy Fastboot on a new device:
+
+### Step 1: Pass the generic Conformance tests
+Begin with just the generic tests (i.e. no XML file). In particular, make sure all
+the conformance tests are passing before you move on. All other tests require that
+the basic generic conformance tests all pass for them to be valid. The conformance
+tests can be run with `./fuzzy_fastboot --gtests_filter=Conformance.*`.
+
+#### Understanding and Fixing Failed Tests
+Whenever a test fails, it will print out to the console the reason for failure
+and the lines and file where the error happened. At the end of each failure
+block, there will usually be a message that Fuzzy Fastboot reports to gtest
+explaining what went wrong. An example is shown below:
+
+```
+Expected equality of these values:
+  resp
+    Which is: "no"
+  unlock ? "yes" : "no"
+    Which is: "yes"
+getvar:unlocked response was not 'no' or 'yes': no
+system/core/fastboot/fuzzy_fastboot/fixtures.cpp:227: Failure
+Expected: SetLockState(UNLOCKED) doesn't generate new fatal failures in the current thread.
+  Actual: it does.
+[THERE WILL BE A MESSAGE HERE EXPLAINING WHY IT FAILED]
+```
+
+In most cases this message at the bottom is all that is needed to figure out why it failed.
+If this is not enough information, below this, gtest will also print out a human readable
+backtrace of the underlying fastboot commands leading up the failure in this test.
+Here is an example:
+```
+<<<<<<<< TRACE BEGIN >>>>>>>>>
+[WRITE 0ms](15 bytes): "getvar:unlocked"
+[READ 20ms](6 bytes): "OKAYno"
+<<<<<<<< TRACE END >>>>>>>>>
+```
+One can easily see the reason for the failure was the test expected the device to
+be unlocked.
+
+If it is still unclear why the failure is happening, the last thing to do is look
+at what line number and file is generating the error. Gtest will always print this out.
+You can then manually look through Fuzzy Fastboot's test source code, and figure out
+what went wrong.
+
+
+### Step 2: Pass all the other generic tests
+Run all the other generic tests (still no XML file). A list of all of them can be
+printed out with: "./fuzzy_fastboot --gtest_list_tests". As before, "--gtest_filter"
+can be used to select certain tests to run, once you figure out which ones failed.
+
+One particular set of tests to watch out for are the ones that involve USB resets.
+USB resets effectively unplug and replug the device in software. Fuzzy Fastboot,
+expects USB resets to cancel whatever transaction is currently going on.
+This is also how Fuzzy Fastboot attempts to recover from errors when the device is
+unresponsive.
+
+### Step 3: Create a device XML configuration
+Without a device specific configuration file, Fuzzy Fastboot will have poor test
+coverage of your device. The vast majority of tests are auto-generated via the XML
+configuration file. Use the guide above to generate a configuration for your device.
+Make sure to be as thorough as possible, and list everything in the configuration
+that can be tested. Finally, make sure that the packed pseudo partitions and
+oem commands all have provided test cases. Be sure to test both the positive case
+(i.e. with valid input), as well as the opposite. Make sure the failure tests
+have good coverage by thinking about all the ways invalid and malicious inputs
+could be formed. These means creating images with malformed headers, illegal chars,
+and other evil inputs.
+
+Now run fuzzy_fastboot with the supplied configuration file. If you do "--gtest_list_tests",
+you should see a ton more tests that were autogenerated by Fuzzy Fastboot.
+As before, run these tests till everything passes. Again, use "--gtest_filter"
+to select specific tests to run once you know what fail,
+as running the whole things with a large configuration can take over 30 minutes.
+See the gtest documentation, for nifty tricks and command line options.
+
+### Step 4: Figure out what Fuzzy Fastboot can't/isn't testing
+While Fuzzy Fastboot with a XML configuration file, should provide good test coverage.
+Think about what device specific things are not being tested, and test them manually.
+In particular, things that if not handled carefully could create security exploits.
+Don't be lazy here, as you already put in the time to get this far.
+
+### Step 5: Celebrate
+You're done :). Now you can be more confident that your implementation is sound, and
+have piece of mind knowing you are protecting the users' security and data by
+running these tests. Don't get too complacent. If the bootloader's source code
+is modified in a way that could introduce bugs or security issues. Make sure to
+test again. You might have to add to your existing configuration file.
+
+## Limitations and Warnings
+- Currently this only works on Linux (even if it builds on Mac)
+- Only fastboot over USB is currently supported
+- Passing these tests does not mean there are not bugs/security issues. For example, a buffer overrun might not always trigger a crash or have any noticeable side effects.
+- **Be extremely careful of the Fuzzy Fastboot tests you are running. Know exactly what the tests do you are about to run before you run them. It is very possible to brick a device with many of these tests.**
+
+## Fuzzy Fastboot Missing Features TODO's
+The following are missing features that should eventually be added
+- *Sparse Image Tests*: Currently there are no tests that tests sparse images. Both well-formed and malicious images need to be tested.
+- *Unlocking/Locking Critical*: Currently there are no tests that tests that locking/unlocking critical functionality.
+- *Saved Test Log*: Fuzzy Fastboot should be able to create a failure log for every failing test and save it to a file. This file should include the test information, the reason it failed, and the fastboot command trace (with the serial console logs). Currently it just prints it to the console at the end of every test.
+- *Host Side Hashing*: One should be able to provide the hashing algorithm to the Fuzzy Fastboot, so it can be checked to agree with what the device is reporting.
+
+
+## Author
+Aaron Wisner - awisner@google.com
diff --git a/fastboot/fuzzy_fastboot/example/checksum_parser.py b/fastboot/fuzzy_fastboot/example/checksum_parser.py
new file mode 100644
index 0000000..1a890e6
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/example/checksum_parser.py
@@ -0,0 +1,37 @@
+'''
+Some bootloader's support hashing partitions. This is a great feature for testing
+correctness. However, the format for the way the hash is returned depends on the
+implementation. The hash could be send through an INFO response, or be as part
+of the OKAY response itself. This script is called with the first argument
+as the string mesage from the okay response. The second argument is each
+info response joined by newlines into one argument.
+'''
+
+import sys
+
+
+def main():
+  '''
+  Data is sent back to the parent fuzzy_fastboot process through the stderr pipe.
+  There are two interpretations of this data by FF.
+
+  0 return code:
+    Anything written to STDERR will be interpreted as part of the hash.
+
+  non-zero return code:
+    Anything written to STDERR is part of the error message that will logged by FF
+    to explain why hash extraction failed.
+
+  Feel free to print to to STDOUT with print() as usual to print info to console
+  '''
+  script, response, info = sys.argv
+  # the info responses are concated by newlines
+  infos = [s.strip() for s in info.splitlines()]
+  sys.stderr.write(infos[-1])
+  print("Extracted checksum: '%s'" % infos[-1])
+  # non-zero return code signals error
+  return 0
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/fastboot/fuzzy_fastboot/example/config.xml b/fastboot/fuzzy_fastboot/example/config.xml
new file mode 100644
index 0000000..af2a3b9
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/example/config.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<config>
+<!-- All the device getvar variables should be listed here -->
+<getvar>
+	<var key="product" assert="superphone2000"/>
+	<var key="secure" assert="no|yes"/>
+</getvar>
+
+<!-- All the device partitions should be listed here -->
+<partitions>
+	<part value="boot" slots="yes" test="yes" hashable="yes" parsed="yes"/>
+	<part value="modem" slots="yes" test="yes" hashable="yes"/>
+	<part value="userdata" slots="no" test="yes" hashable="no"/>
+
+	<!-- Bootloader partitions -->
+	<part value="foo1" slots="yes" test="no" hashable="yes"/>
+	<part value="foo2" slots="yes" test="no" hashable="yes"/>
+	<part value="bar3" slots="yes" test="no" hashable="yes"/>
+</partitions>
+
+<!-- All the device packed partitions should be listed here -->
+<packed>
+	<part value="bootloader" slots="yes">
+		<!-- We list the real partitions it is composed of -->
+		<child>foo1</child>
+		<child>foo2</child>
+		<child>bar3</child>
+		<!-- We list tests, expect defaults to 'okay' -->
+		<test packed="bootloader.img" unpacked="unpacked"/>
+		<test packed="bootloader_garbage.img" expect="fail"/>
+	</part>
+</packed>
+
+<!-- All the oem commands should be listed here -->
+<oem>
+	<!-- The 'oem self_destruct' command requires an unlocked bootloader -->
+	<command value="self_destruct" permissions="unlocked">
+		<!-- This will test that "oem self_destruct now" returns 'okay' -->
+		<test value="now" expect="okay"/>
+		<test value="yesterday" expect="fail" />
+	</command>
+
+	<!-- Test a fictional 'oem get' command -->
+	<command value="get" permissions="none">
+		<test value="batch_id" expect="okay" assert="[[:digit:]]+"/>
+		<test value="device_color" expect="okay" assert="green|blue"/>
+		<test value="build_num" expect="okay" assert="[\w\-.]+"/>
+		<test value="garbage" expect="fail" assert="Invalid var '[\w ]+'"/>
+	</command>
+
+	<!-- Some oem commands might require staging or downloading data, or both -->
+	<command value="foobar" permissions="unlocked">
+		<!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->
+		<test value="use_staged" expect="okay" input="test_image.img" />
+		<!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->
+		<test value="send_response" expect="fail" validate="python validator.py"/>
+	</command>
+</oem>
+
+<!-- If there is a custom oem checksum command to hash partitions, add it here -->
+<checksum value="oem sha1sum"/>
+</config>
diff --git a/fastboot/fuzzy_fastboot/example/validator.py b/fastboot/fuzzy_fastboot/example/validator.py
new file mode 100644
index 0000000..9c5081f
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/example/validator.py
@@ -0,0 +1,37 @@
+'''
+This is an example validator to be used with oem commands that allow you to
+upload data afterwards that you wish to validate locally.
+'''
+import sys
+
+def eprint(msg):
+  '''
+  A helper function for logging error messages to fuzzy_fastboot
+  Use this function as you would "print()"
+  '''
+  sys.stderr.write(msg + '\n')
+
+
+def main():
+  '''
+  Data is sent back to the parent fuzzy_fastboot process through the stderr pipe.
+
+  If this script has a non-zero return code, anything written to STDERR is part of
+  the error message that will logged by FF to explain why this validation failed.
+
+  Feel free to print to to STDOUT with print() as usual to print info to console
+  '''
+  script, command, fname = sys.argv
+  eprint("Messages here will go to the parent testers logs")
+  eprint("Hello world")
+  print("This goes to stdout as expected")
+  with open(fname, "rb") as fd:
+    # Do some validation on the buffer
+    pass
+
+  # non-zero return code signals error
+  return -1
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/fastboot/fuzzy_fastboot/extensions.cpp b/fastboot/fuzzy_fastboot/extensions.cpp
new file mode 100644
index 0000000..62ef5ba
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/extensions.cpp
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <csignal>
+#include <cstdlib>
+#include <fstream>
+
+#include "extensions.h"
+#include "test_utils.h"
+#include "tinyxml2.h"
+
+namespace fastboot {
+namespace extension {
+
+namespace {  // private to this file
+
+// Since exceptions are disabled, a bad regex will trigger an abort in the constructor
+// We at least need to print something out
+std::regex MakeRegex(const std::string& regex_str, int line_num,
+                     std::regex_constants::syntax_option_type type = std::regex::ECMAScript) {
+    // The signal handler can only access static vars
+    static std::string err_str;
+    err_str = android::base::StringPrintf("'%s' is not a valid regex string (line %d)\n",
+                                          regex_str.c_str(), line_num);
+    const auto sighandler = [](int) {
+        int nbytes = write(fileno(stderr), err_str.c_str(), err_str.length());
+        static_cast<void>(nbytes);  // need to supress the unused nbytes/ or unused result
+    };
+    std::signal(SIGABRT, sighandler);
+    // Now attempt to create the regex
+    std::regex ret(regex_str, type);
+    // unregister
+    std::signal(SIGABRT, SIG_DFL);
+
+    return ret;
+}
+
+bool XMLAssert(bool cond, const tinyxml2::XMLElement* elem, const char* msg) {
+    if (!cond) {
+        printf("%s (line %d)\n", msg, elem->GetLineNum());
+    }
+    return !cond;
+}
+
+const std::string XMLAttribute(const tinyxml2::XMLElement* elem, const std::string key,
+                               const std::string key_default = "") {
+    if (!elem->Attribute(key.c_str())) {
+        return key_default;
+    }
+
+    return elem->Attribute(key.c_str());
+}
+
+bool XMLYesNo(const tinyxml2::XMLElement* elem, const std::string key, bool* res,
+              bool def = false) {
+    if (!elem->Attribute(key.c_str())) {
+        *res = def;
+        return true;
+    }
+    const std::string val = elem->Attribute(key.c_str());
+    if (val != "yes" && val != "no") {
+        return false;
+    }
+    *res = (val == "yes");
+    return true;
+}
+
+bool ExtractPartitions(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    // Extract partitions
+    const tinyxml2::XMLElement* part = handle.FirstChildElement("part").ToElement();
+    while (part) {
+        Configuration::PartitionInfo part_info;
+        const std::string name = XMLAttribute(part, "value");
+        const std::string test = XMLAttribute(part, "test");
+        if (XMLAssert(!name.empty(), part, "The name of a partition can not be empty") ||
+            XMLAssert(XMLYesNo(part, "slots", &part_info.slots), part,
+                      "Slots attribute must be 'yes' or 'no'") ||
+            XMLAssert(XMLYesNo(part, "hashable", &part_info.hashable, true), part,
+                      "Hashable attribute must be 'yes' or 'no'") ||
+            XMLAssert(XMLYesNo(part, "parsed", &part_info.parsed), part,
+                      "Parsed attribute must be 'yes' or 'no'"))
+            return false;
+
+        bool allowed = test == "yes" || test == "no-writes" || test == "no";
+        if (XMLAssert(allowed, part, "The test attribute must be 'yes' 'no-writes' or 'no'"))
+            return false;
+        if (XMLAssert(config->partitions.find(name) == config->partitions.end(), part,
+                      "The same partition name is listed twice"))
+            return false;
+        part_info.test = (test == "yes")
+                                 ? Configuration::PartitionInfo::YES
+                                 : (test == "no-writes") ? Configuration::PartitionInfo::NO_WRITES
+                                                         : Configuration::PartitionInfo::NO;
+        config->partitions[name] = part_info;
+        part = part->NextSiblingElement("part");
+    }
+    return true;
+}
+
+bool ExtractPacked(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    // Extract partitions
+    const tinyxml2::XMLElement* part = handle.FirstChildElement("part").ToElement();
+    while (part) {
+        Configuration::PackedInfo packed_info;
+        const std::string name = XMLAttribute(part, "value");
+
+        if (XMLAssert(!name.empty(), part, "The name of a packed partition can not be empty") ||
+            XMLAssert(XMLYesNo(part, "slots", &packed_info.slots), part,
+                      "Slots attribute must be 'yes' or 'no'") ||
+            XMLAssert(config->partitions.find(name) == config->partitions.end(), part,
+                      "A packed partition can not have same name as a real one") ||
+            XMLAssert(config->partitions.find(name) == config->partitions.end(), part,
+                      "The same partition name is listed twice"))
+            return false;
+
+        // Extract children partitions
+        const tinyxml2::XMLElement* child = part->FirstChildElement("child")
+                                                    ? part->FirstChildElement("child")->ToElement()
+                                                    : nullptr;
+        while (child) {
+            const std::string text(child->GetText());
+            // Make sure child exists
+            if (XMLAssert(config->partitions.find(text) != config->partitions.end(), child,
+                          "The child partition was not listed in <partitions>"))
+                return false;
+            packed_info.children.insert(text);
+            child = child->NextSiblingElement("child");
+        }
+
+        // Extract tests
+        const tinyxml2::XMLElement* test = part->FirstChildElement("test")
+                                                   ? part->FirstChildElement("test")->ToElement()
+                                                   : nullptr;
+        while (test) {
+            Configuration::PackedInfoTest packed_test;
+            packed_test.packed_img = XMLAttribute(test, "packed");
+            packed_test.unpacked_dir = XMLAttribute(test, "unpacked");
+            const std::string expect = XMLAttribute(test, "expect", "okay");
+
+            if (XMLAssert(!packed_test.packed_img.empty(), test,
+                          "The packed image location must be specified") ||
+                XMLAssert(CMD_EXPECTS.find(expect) != CMD_EXPECTS.end(), test,
+                          "Expect attribute must be 'okay' or 'fail'"))
+                return false;
+
+            packed_test.expect = CMD_EXPECTS.at(expect);
+            // The expect is only unpacked directory is only needed if success
+            if (packed_test.expect == OKAY &&
+                XMLAssert(!packed_test.unpacked_dir.empty(), test,
+                          "The unpacked image folder location must be specified"))
+                return false;
+
+            packed_info.tests.push_back(packed_test);
+            test = test->NextSiblingElement("test");
+        }
+
+        config->packed[name] = packed_info;
+        part = part->NextSiblingElement("part");
+    }
+    return true;
+}
+
+bool ExtractGetVars(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    // Extract getvars
+    const tinyxml2::XMLElement* var = handle.FirstChildElement("var").ToElement();
+    while (var) {
+        const std::string key = XMLAttribute(var, "key");
+        const std::string reg = XMLAttribute(var, "assert");
+        if (XMLAssert(key.size(), var, "The var key name is empty")) return false;
+        if (XMLAssert(config->getvars.find(key) == config->getvars.end(), var,
+                      "The same getvar variable name is listed twice"))
+            return false;
+        Configuration::GetVar getvar{reg, MakeRegex(reg, var->GetLineNum()), var->GetLineNum()};
+        config->getvars[key] = std::move(getvar);
+        var = var->NextSiblingElement("var");
+    }
+    return true;
+}
+
+bool ExtractOem(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    // Extract getvars
+    // Extract oem commands
+    const tinyxml2::XMLElement* command = handle.FirstChildElement("command").ToElement();
+    while (command) {
+        const std::string cmd = XMLAttribute(command, "value");
+        const std::string permissions = XMLAttribute(command, "permissions");
+        if (XMLAssert(cmd.size(), command, "Empty command value")) return false;
+        if (XMLAssert(permissions == "none" || permissions == "unlocked", command,
+                      "Permissions attribute must be 'none' or 'unlocked'"))
+            return false;
+
+        // Each command has tests
+        std::vector<Configuration::CommandTest> tests;
+        const tinyxml2::XMLElement* test = command->FirstChildElement("test");
+        while (test) {  // iterate through tests
+            Configuration::CommandTest ctest;
+
+            ctest.line_num = test->GetLineNum();
+            const std::string default_name = "XMLTest-line-" + std::to_string(test->GetLineNum());
+            ctest.name = XMLAttribute(test, "name", default_name);
+            ctest.arg = XMLAttribute(test, "value");
+            ctest.input = XMLAttribute(test, "input");
+            ctest.output = XMLAttribute(test, "output");
+            ctest.validator = XMLAttribute(test, "validate");
+            ctest.regex_str = XMLAttribute(test, "assert");
+
+            const std::string expect = XMLAttribute(test, "expect");
+
+            if (XMLAssert(CMD_EXPECTS.find(expect) != CMD_EXPECTS.end(), test,
+                          "Expect attribute must be 'okay' or 'fail'"))
+                return false;
+            ctest.expect = CMD_EXPECTS.at(expect);
+            std::regex regex;
+            if (expect == "okay" && ctest.regex_str.size()) {
+                ctest.regex = MakeRegex(ctest.regex_str, test->GetLineNum());
+            }
+            tests.push_back(std::move(ctest));
+            test = test->NextSiblingElement("test");
+        }
+
+        // Build the command struct
+        const Configuration::OemCommand oem_cmd{permissions == "unlocked", std::move(tests)};
+        config->oem[cmd] = std::move(oem_cmd);
+
+        command = command->NextSiblingElement("command");
+    }
+    return true;
+}
+
+bool ExtractChecksum(tinyxml2::XMLConstHandle handle, Configuration* config) {
+    const tinyxml2::XMLElement* checksum = handle.ToElement();
+    if (checksum && checksum->Attribute("value")) {
+        config->checksum = XMLAttribute(checksum, "value");
+        config->checksum_parser = XMLAttribute(checksum, "parser");
+        if (XMLAssert(config->checksum_parser != "", checksum,
+                      "A checksum parser attribute is mandatory"))
+            return false;
+    }
+    return true;
+}
+
+}  // anonymous namespace
+
+bool ParseXml(const std::string& file, Configuration* config) {
+    tinyxml2::XMLDocument doc;
+    if (doc.LoadFile(file.c_str())) {
+        printf("Failed to open/parse XML file '%s'\nXMLError: %s\n", file.c_str(), doc.ErrorStr());
+        return false;
+    }
+
+    tinyxml2::XMLConstHandle handle(&doc);
+    tinyxml2::XMLConstHandle root(handle.FirstChildElement("config"));
+
+    // Extract the getvars
+    if (!ExtractGetVars(root.FirstChildElement("getvar"), config)) {
+        return false;
+    }
+    // Extract the partition info
+    if (!ExtractPartitions(root.FirstChildElement("partitions"), config)) {
+        return false;
+    }
+
+    // Extract packed
+    if (!ExtractPacked(root.FirstChildElement("packed"), config)) {
+        return false;
+    }
+
+    // Extract oem commands
+    if (!ExtractOem(root.FirstChildElement("oem"), config)) {
+        return false;
+    }
+
+    // Extract checksum
+    if (!ExtractChecksum(root.FirstChildElement("checksum"), config)) {
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace extension
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/extensions.h b/fastboot/fuzzy_fastboot/extensions.h
new file mode 100644
index 0000000..58312e5
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/extensions.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#pragma once
+
+#include <regex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+namespace fastboot {
+namespace extension {
+
+enum Expect { OKAY = 0, FAIL, DATA };
+
+static const std::unordered_map<std::string, Expect> CMD_EXPECTS = {
+        {"okay", OKAY},
+        {"fail", FAIL},
+        {"data", DATA},
+};
+
+static const std::unordered_map<Expect, std::string> EXPECTS_STR = {
+        {OKAY, "okay"},
+        {FAIL, "fail"},
+        {DATA, "data"},
+};
+
+struct Configuration {
+    struct GetVar {
+        std::string regex_str;
+        std::regex regex;
+        int line_num;
+
+        // So gtest can print me
+        friend ::std::ostream& operator<<(::std::ostream& os, const GetVar& self) {
+            return os << "<regex='" << self.regex_str << "' line_num=" << self.line_num << ">";
+        }
+    };
+    struct PartitionInfo {
+        enum TestConfig { NO = 0, NO_WRITES, YES };
+        bool hashable;
+        bool slots;   // Does it have slots
+        bool parsed;  // Does the bootloader do parsing on the img?
+        TestConfig test;
+
+        // So gtest can print me
+        friend ::std::ostream& operator<<(::std::ostream& os, const PartitionInfo& pinfo) {
+            return os << "<hashable=" << pinfo.hashable << " slots=" << pinfo.slots
+                      << " parsed=" << pinfo.parsed << ">";
+        }
+    };
+
+    struct PackedInfoTest {
+        Expect expect;  // Does it have slots
+        std::string packed_img;
+        std::string unpacked_dir;
+
+        // So gtest can print me
+        friend ::std::ostream& operator<<(::std::ostream& os, const PackedInfoTest& pinfo) {
+            return os << "<"
+                      << "expect=" << EXPECTS_STR.at(pinfo.expect)
+                      << " packed_img=" << pinfo.packed_img
+                      << " unpacked_dir=" << pinfo.unpacked_dir << ">";
+        }
+    };
+
+    struct PackedInfo {
+        bool slots;  // Does it have slots
+        std::unordered_set<std::string> children;
+        std::vector<PackedInfoTest> tests;
+    };
+
+    struct CommandTest {
+        std::string name;
+        int line_num;
+        std::string arg;
+        Expect expect;
+        std::string regex_str;
+        std::regex regex;
+        std::string input;
+        std::string output;
+        std::string validator;
+
+        // So gtest can print me
+        friend ::std::ostream& operator<<(::std::ostream& os, const CommandTest& self) {
+            return os << "test: " << self.name << " (line: " << self.line_num << ")";
+        }
+    };
+
+    struct OemCommand {
+        bool restricted;  // Does device need to be unlocked?
+        std::vector<CommandTest> tests;
+    };
+
+    std::unordered_map<std::string, GetVar> getvars;
+    std::unordered_map<std::string, PartitionInfo> partitions;
+    std::unordered_map<std::string, PackedInfo> packed;
+    std::unordered_map<std::string, OemCommand> oem;
+
+    std::string checksum;
+    std::string checksum_parser;
+};
+
+bool ParseXml(const std::string& file, Configuration* config);
+
+}  // namespace extension
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
new file mode 100644
index 0000000..4da71ca
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+#include <chrono>
+#include <cstdlib>
+#include <fstream>
+#include <map>
+#include <random>
+#include <regex>
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "fastboot_driver.h"
+#include "usb.h"
+
+#include "extensions.h"
+#include "fixtures.h"
+#include "test_utils.h"
+#include "usb_transport_sniffer.h"
+
+namespace fastboot {
+
+int FastBootTest::MatchFastboot(usb_ifc_info* info, const char* local_serial) {
+    if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
+        return -1;
+    }
+
+    cb_scratch = info->device_path;
+
+    // require matching serial number or device path if requested
+    // at the command line with the -s option.
+    if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
+                         strcmp(local_serial, info->device_path) != 0))
+        return -1;
+    return 0;
+}
+
+bool FastBootTest::UsbStillAvailible() {
+    // For some reason someone decided to prefix the path with "usb:"
+    std::string prefix("usb:");
+    if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {
+        std::string fname(device_path.begin() + prefix.size(), device_path.end());
+        std::string real_path =
+                android::base::StringPrintf("/sys/bus/usb/devices/%s/serial", fname.c_str());
+        std::ifstream f(real_path.c_str());
+        return f.good();
+    }
+    exit(-1);  // This should never happen
+    return true;
+}
+
+RetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,
+                                      std::vector<std::string>* info) {
+    return fb->DownloadCommand(size, response, info);
+}
+
+RetCode FastBootTest::SendBuffer(const std::vector<char>& buf) {
+    return fb->SendBuffer(buf);
+}
+
+RetCode FastBootTest::HandleResponse(std::string* response, std::vector<std::string>* info,
+                                     int* dsize) {
+    return fb->HandleResponse(response, info, dsize);
+}
+
+void FastBootTest::SetUp() {
+    if (device_path != "") {               // make sure the device is still connected
+        ASSERT_TRUE(UsbStillAvailible());  // The device disconnected
+    }
+
+    const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
+    for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
+        std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+        if (usb)
+            transport = std::unique_ptr<UsbTransportSniffer>(
+                    new UsbTransportSniffer(std::move(usb), serial_port));
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    }
+
+    ASSERT_TRUE(transport);  // no nullptr
+
+    if (device_path == "") {  // We set it the first time, then make sure it never changes
+        device_path = cb_scratch;
+    } else {
+        ASSERT_EQ(device_path, cb_scratch);  // The path can not change
+    }
+    fb = std::unique_ptr<FastBootDriver>(
+            new FastBootDriver(transport.get(), [](std::string&) {}, true));
+}
+
+void FastBootTest::TearDown() {
+    EXPECT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+
+    TearDownSerial();
+
+    fb.reset();
+
+    if (transport) {
+        transport.reset();
+    }
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+}
+
+// TODO, this should eventually be piped to a file instead of stdout
+void FastBootTest::TearDownSerial() {
+    if (!transport) return;
+    // One last read from serial
+    transport->ProcessSerial();
+    if (HasFailure()) {
+        // TODO, print commands leading up
+        printf("<<<<<<<< TRACE BEGIN >>>>>>>>>\n");
+        printf("%s", transport->CreateTrace().c_str());
+        printf("<<<<<<<< TRACE END >>>>>>>>>\n");
+        // std::vector<std::pair<const TransferType, const std::vector<char>>>  prev =
+        // transport->Transfers();
+    }
+}
+
+void FastBootTest::SetLockState(bool unlock, bool assert_change) {
+    if (!fb) {
+        return;
+    }
+
+    std::string resp;
+    std::vector<std::string> info;
+    // To avoid risk of bricking device, make sure unlock ability is set to 1
+    ASSERT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
+            << "'flashing get_unlock_ability' failed";
+
+    // There are two ways this can be reported, through info or the actual response
+    if (!resp.empty()) {  // must be in the info response
+        ASSERT_EQ(resp.back(), '1')
+                << "Unlock ability must be set to 1 to avoid bricking device, see "
+                   "'https://source.android.com/devices/bootloader/unlock-trusty'";
+    } else {
+        ASSERT_FALSE(info.empty()) << "'flashing get_unlock_ability' returned empty response";
+        ASSERT_FALSE(info.back().empty()) << "Expected non-empty info response";
+        ASSERT_EQ(info.back().back(), '1')
+                << "Unlock ability must be set to 1 to avoid bricking device, see "
+                   "'https://source.android.com/devices/bootloader/unlock-trusty'";
+    }
+
+    EXPECT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+    ASSERT_TRUE(resp == "no" || resp == "yes")
+            << "getvar:unlocked response was not 'no' or 'yes': " + resp;
+
+    if ((unlock && resp == "no") || (!unlock && resp == "yes")) {
+        std::string cmd = unlock ? "unlock" : "lock";
+        ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
+                << "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
+        fb.reset();
+        transport.reset();
+        printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
+        while (UsbStillAvailible())
+            ;  // Wait for disconnect
+        printf("WAITING FOR DEVICE");
+        // Need to wait for device
+        const auto matcher = [](usb_ifc_info* info) -> int { return MatchFastboot(info, nullptr); };
+        while (!transport) {
+            std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+            if (usb) {
+                transport = std::unique_ptr<UsbTransportSniffer>(
+                        new UsbTransportSniffer(std::move(usb), serial_port));
+            }
+            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+            putchar('.');
+        }
+        device_path = cb_scratch;
+        fb = std::unique_ptr<FastBootDriver>(
+                new FastBootDriver(transport.get(), [](std::string&) {}, true));
+        if (assert_change) {
+            ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+            ASSERT_EQ(resp, unlock ? "yes" : "no")
+                    << "getvar:unlocked response was not 'no' or 'yes': " + resp;
+        }
+        printf("SUCCESS\n");
+    }
+}
+
+std::string FastBootTest::device_path = "";
+std::string FastBootTest::cb_scratch = "";
+int FastBootTest::serial_port = 0;
+
+template <bool UNLOCKED>
+void ModeTest<UNLOCKED>::SetUp() {
+    ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
+    ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
+}
+// Need to instatiate it, so linker can find it later
+template class ModeTest<true>;
+template class ModeTest<false>;
+
+void Fuzz::TearDown() {
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+
+    TearDownSerial();
+
+    std::string tmp;
+    if (fb->GetVar("product", &tmp) != SUCCESS) {
+        printf("DEVICE UNRESPONSE, attempting to recover...");
+        transport->Reset();
+        printf("issued USB reset...");
+
+        if (fb->GetVar("product", &tmp) != SUCCESS) {
+            printf("FAIL\n");
+            exit(-1);
+        }
+        printf("SUCCESS!\n");
+    }
+
+    if (transport) {
+        transport.reset();
+    }
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+}
+
+template <bool UNLOCKED>
+void ExtensionsPartition<UNLOCKED>::SetUp() {
+    ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
+    ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
+
+    if (!fb) {
+        return;
+    }
+    const std::string name = GetParam().first;
+
+    std::string var;
+    ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
+    int32_t num_slots = strtol(var.c_str(), nullptr, 10);
+    real_parts = GeneratePartitionNames(name, GetParam().second.slots ? num_slots : 0);
+
+    ASSERT_EQ(fb->GetVar("partition-size:" + real_parts.front(), &var), SUCCESS)
+            << "Getting partition size failed";
+    part_size = strtoll(var.c_str(), nullptr, 16);
+    ASSERT_GT(part_size, 0) << "Partition size reported was invalid";
+
+    ASSERT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "Getting max download size failed";
+    max_dl = strtoll(var.c_str(), nullptr, 16);
+    ASSERT_GT(max_dl, 0) << "Max download size reported was invalid";
+
+    max_flash = std::min(part_size, max_dl);
+}
+template class ExtensionsPartition<true>;
+template class ExtensionsPartition<false>;
+
+}  // end namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
new file mode 100644
index 0000000..e47d0fd
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#pragma once
+#include <gtest/gtest.h>
+
+#include "fastboot_driver.h"
+
+#include "extensions.h"
+#include "usb_transport_sniffer.h"
+
+namespace fastboot {
+
+const int USB_TIMEOUT = 30000;
+
+constexpr char USB_PORT_GONE[] =
+        "The USB port has disappeared, this is usually due to the bootloader crashing";
+
+class FastBootTest : public testing::Test {
+  public:
+    static int serial_port;
+    static constexpr int MAX_USB_TRIES = 10;
+
+    static int MatchFastboot(usb_ifc_info* info, const char* local_serial = nullptr);
+    bool UsbStillAvailible();
+
+  protected:
+    RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
+                            std::vector<std::string>* info = nullptr);
+
+    RetCode SendBuffer(const std::vector<char>& buf);
+    RetCode HandleResponse(std::string* response = nullptr,
+                           std::vector<std::string>* info = nullptr, int* dsize = nullptr);
+
+    void SetUp() override;
+    void TearDown() override;
+    void TearDownSerial();
+    void SetLockState(bool unlock, bool assert_change = true);
+
+    std::unique_ptr<UsbTransportSniffer> transport;
+    std::unique_ptr<FastBootDriver> fb;
+
+  private:
+    // This is an annoying hack
+    static std::string cb_scratch;
+    static std::string device_path;
+};
+
+template <bool UNLOCKED>
+class ModeTest : public FastBootTest {
+  protected:
+    void SetUp() override;
+};
+
+class Fuzz : public ModeTest<true> {
+  protected:
+    void TearDown() override;
+};
+
+// These derived classes without overrides serve no purpose other than to allow gtest to name them
+// differently
+class BasicFunctionality : public ModeTest<true> {};
+class Conformance : public ModeTest<true> {};
+class UnlockPermissions : public ModeTest<true> {};
+class LockPermissions : public ModeTest<false> {};
+
+// Magic C++ double inheritance
+class ExtensionsGetVarConformance
+    : public ModeTest<true>,
+      public ::testing::WithParamInterface<
+              std::pair<std::string, extension::Configuration::GetVar>> {};
+
+class ExtensionsOemConformance
+    : public ModeTest<true>,
+      public ::testing::WithParamInterface<
+              std::tuple<std::string, bool, extension::Configuration::CommandTest>> {};
+
+class ExtensionsPackedValid
+    : public ModeTest<true>,
+      public ::testing::WithParamInterface<
+              std::pair<std::string, extension::Configuration::PackedInfoTest>> {};
+
+class ExtensionsPackedInvalid
+    : public ModeTest<true>,
+      public ::testing::WithParamInterface<
+              std::pair<std::string, extension::Configuration::PackedInfoTest>> {};
+
+template <bool UNLOCKED>
+class ExtensionsPartition
+    : public FastBootTest,
+      public ::testing::WithParamInterface<
+              std::pair<std::string, extension::Configuration::PartitionInfo>> {
+  protected:
+    void SetUp() override;
+    int64_t part_size;
+    int64_t max_flash;
+    int64_t max_dl;
+    std::vector<std::string> real_parts;  // includes the slots
+};
+
+class AnyPartition : public ExtensionsPartition<true> {};
+class WriteablePartition : public ExtensionsPartition<true> {};
+class WriteHashablePartition : public ExtensionsPartition<true> {};
+class WriteHashNonParsedPartition : public ExtensionsPartition<true> {};
+
+class FuzzWriteablePartition : public ExtensionsPartition<true> {};
+class FuzzWriteableParsedPartition : public ExtensionsPartition<true> {};
+class FuzzAnyPartitionLocked : public ExtensionsPartition<false> {};
+
+class UserdataPartition : public ExtensionsPartition<true> {};
+
+class SparseTestPartition : public ExtensionsPartition<true> {};
+
+}  // end namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
new file mode 100644
index 0000000..8fb5a6a
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -0,0 +1,1690 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <chrono>
+#include <cstdlib>
+#include <fstream>
+#include <map>
+#include <random>
+#include <regex>
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <sparse/sparse.h>
+
+#include "fastboot_driver.h"
+#include "usb.h"
+
+#include "extensions.h"
+#include "fixtures.h"
+#include "test_utils.h"
+#include "usb_transport_sniffer.h"
+
+namespace fastboot {
+
+extension::Configuration config;  // The parsed XML config
+
+std::string SEARCH_PATH;
+std::string OUTPUT_PATH;
+
+// gtest's INSTANTIATE_TEST_CASE_P() must be at global scope,
+// so our autogenerated tests must be as well
+std::vector<std::pair<std::string, extension::Configuration::GetVar>> GETVAR_XML_TESTS;
+std::vector<std::tuple<std::string, bool, extension::Configuration::CommandTest>> OEM_XML_TESTS;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>> PARTITION_XML_TESTS;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_WRITEABLE;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_WRITE_HASHABLE;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_WRITE_PARSED;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_WRITE_HASH_NONPARSED;
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE;
+std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>>
+        PACKED_XML_SUCCESS_TESTS;
+std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>> PACKED_XML_FAIL_TESTS;
+// This only has 1 or zero elements so it will disappear from gtest when empty
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+        SINGLE_PARTITION_XML_WRITE_HASHABLE;
+
+const std::string DEFAULT_OUPUT_NAME = "out.img";
+// const char scratch_partition[] = "userdata";
+const std::vector<std::string> CMDS{"boot",    "continue", "download:",   "erase:", "flash:",
+                                    "getvar:", "reboot",   "set_active:", "upload"};
+
+// For pretty printing we need all these overloads
+::std::ostream& operator<<(::std::ostream& os, const RetCode& ret) {
+    return os << FastBootDriver::RCString(ret);
+}
+
+bool PartitionHash(FastBootDriver* fb, const std::string& part, std::string* hash, int* retcode,
+                   std::string* err_msg) {
+    if (config.checksum.empty()) {
+        return -1;
+    }
+
+    std::string resp;
+    std::vector<std::string> info;
+    const std::string cmd = config.checksum + ' ' + part;
+    RetCode ret;
+    if ((ret = fb->RawCommand(cmd, &resp, &info)) != SUCCESS) {
+        *err_msg =
+                android::base::StringPrintf("Hashing partition with command '%s' failed with: %s",
+                                            cmd.c_str(), fb->RCString(ret).c_str());
+        return false;
+    }
+    std::stringstream imploded;
+    std::copy(info.begin(), info.end(), std::ostream_iterator<std::string>(imploded, "\n"));
+
+    // If payload, we validate that as well
+    const std::vector<std::string> args = SplitBySpace(config.checksum_parser);
+    std::vector<std::string> prog_args(args.begin() + 1, args.end());
+    prog_args.push_back(resp);                          // Pass in the full command
+    prog_args.push_back(SEARCH_PATH + imploded.str());  // Pass in the save location
+
+    int pipe;
+    pid_t pid = StartProgram(args[0], prog_args, &pipe);
+    if (pid <= 0) {
+        *err_msg = android::base::StringPrintf("Launching hash parser '%s' failed with: %s",
+                                               config.checksum_parser.c_str(), strerror(errno));
+        return false;
+    }
+    *retcode = WaitProgram(pid, pipe, hash);
+    if (*retcode) {
+        // In this case the stderr pipe is a log message
+        *err_msg = android::base::StringPrintf("Hash parser '%s' failed with: %s",
+                                               config.checksum_parser.c_str(), hash->c_str());
+        return false;
+    }
+
+    return true;
+}
+
+bool SparseToBuf(sparse_file* sf, std::vector<char>* out, bool with_crc = false) {
+    int64_t len = sparse_file_len(sf, true, with_crc);
+    if (len <= 0) {
+        return false;
+    }
+    out->clear();
+    auto cb = [](void* priv, const void* data, size_t len) {
+        auto vec = static_cast<std::vector<char>*>(priv);
+        const char* cbuf = static_cast<const char*>(data);
+        vec->insert(vec->end(), cbuf, cbuf + len);
+        return 0;
+    };
+
+    return !sparse_file_callback(sf, true, with_crc, cb, out);
+}
+
+// Only allow alphanumeric, _, -, and .
+const auto not_allowed = [](char c) -> int {
+    return !(isalnum(c) || c == '_' || c == '-' || c == '.');
+};
+
+// Test that USB even works
+TEST(USBFunctionality, USBConnect) {
+    const auto matcher = [](usb_ifc_info* info) -> int {
+        return FastBootTest::MatchFastboot(info, nullptr);
+    };
+    Transport* transport = nullptr;
+    for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
+        transport = usb_open(matcher);
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    }
+    ASSERT_NE(transport, nullptr) << "Could not find the fastboot device after: "
+                                  << 10 * FastBootTest::MAX_USB_TRIES << "ms";
+    if (transport) {
+        transport->Close();
+        delete transport;
+    }
+}
+
+// Conformance tests
+TEST_F(Conformance, GetVar) {
+    std::string product;
+    EXPECT_EQ(fb->GetVar("product", &product), SUCCESS) << "getvar:product failed";
+    EXPECT_NE(product, "") << "getvar:product response was empty string";
+    EXPECT_EQ(std::count_if(product.begin(), product.end(), not_allowed), 0)
+            << "getvar:product response contained illegal chars";
+    EXPECT_LE(product.size(), FB_RESPONSE_SZ - 4) << "getvar:product response was too large";
+}
+
+TEST_F(Conformance, GetVarVersionBootloader) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("version-bootloader", &var), SUCCESS)
+            << "getvar:version-bootloader failed";
+    EXPECT_NE(var, "") << "getvar:version-bootloader response was empty string";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
+            << "getvar:version-bootloader response contained illegal chars";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:version-bootloader response was too large";
+}
+
+TEST_F(Conformance, GetVarVersionBaseband) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("version-baseband", &var), SUCCESS) << "getvar:version-baseband failed";
+    EXPECT_NE(var, "") << "getvar:version-baseband response was empty string";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
+            << "getvar:version-baseband response contained illegal chars";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:version-baseband response was too large";
+}
+
+TEST_F(Conformance, GetVarSerialNo) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("serialno", &var), SUCCESS) << "getvar:serialno failed";
+    EXPECT_NE(var, "") << "getvar:serialno can not be empty string";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), isalnum), var.size())
+            << "getvar:serialno must be alpha-numeric";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:serialno response is too long";
+}
+
+TEST_F(Conformance, GetVarSecure) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("secure", &var), SUCCESS);
+    EXPECT_TRUE(var == "yes" || var == "no");
+}
+
+TEST_F(Conformance, GetVarOffModeCharge) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("off-mode-charge", &var), SUCCESS) << "getvar:off-mode-charge failed";
+    EXPECT_TRUE(var == "0" || var == "1") << "getvar:off-mode-charge response must be '0' or '1'";
+}
+
+TEST_F(Conformance, GetVarVariant) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("variant", &var), SUCCESS) << "getvar:variant failed";
+    EXPECT_NE(var, "") << "getvar:variant response can not be empty";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:variant response is too large";
+}
+
+TEST_F(Conformance, GetVarRevision) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("hw-revision", &var), SUCCESS) << "getvar:hw-revision failed";
+    EXPECT_NE(var, "") << "getvar:battery-voltage response was empty";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
+            << "getvar:hw-revision contained illegal ASCII chars";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:hw-revision response was too large";
+}
+
+TEST_F(Conformance, GetVarBattVoltage) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("battery-voltage", &var), SUCCESS) << "getvar:battery-voltage failed";
+    EXPECT_NE(var, "") << "getvar:battery-voltage response was empty";
+    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
+            << "getvar:battery-voltage response contains illegal ASCII chars";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4)
+            << "getvar:battery-voltage response is too large: " + var;
+}
+
+TEST_F(Conformance, GetVarBattVoltageOk) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("battery-soc-ok", &var), SUCCESS) << "getvar:battery-soc-ok failed";
+    EXPECT_TRUE(var == "yes" || var == "no") << "getvar:battery-soc-ok must be 'yes' or 'no'";
+}
+
+TEST_F(Conformance, GetVarDownloadSize) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    EXPECT_NE(var, "") << "getvar:max-download-size responded with empty string";
+    // This must start with 0x
+    EXPECT_FALSE(isspace(var.front()))
+            << "getvar:max-download-size responded with a string with leading whitespace";
+    EXPECT_FALSE(var.compare(0, 2, "0x"))
+            << "getvar:max-download-size responded with a string that does not start with 0x...";
+    int64_t size = strtoll(var.c_str(), nullptr, 16);
+    EXPECT_GT(size, 0) << "'" + var + "' is not a valid response from getvar:max-download-size";
+    // At most 32-bits
+    EXPECT_LE(size, std::numeric_limits<uint32_t>::max())
+            << "getvar:max-download-size must fit in a uint32_t";
+    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4)
+            << "getvar:max-download-size responded with too large of string: " + var;
+}
+
+TEST_F(Conformance, GetVarAll) {
+    std::vector<std::string> vars;
+    EXPECT_EQ(fb->GetVarAll(&vars), SUCCESS) << "getvar:all failed";
+    EXPECT_GT(vars.size(), 0) << "getvar:all did not respond with any INFO responses";
+    for (const auto s : vars) {
+        EXPECT_LE(s.size(), FB_RESPONSE_SZ - 4)
+                << "getvar:all included an INFO response: 'INFO" + s << "' which is too long";
+    }
+}
+
+TEST_F(Conformance, UnlockAbility) {
+    std::string resp;
+    std::vector<std::string> info;
+    EXPECT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
+            << "'flashing get_unlock_ability' failed";
+    // There are two ways this can be reported, through info or the actual response
+    char last;
+    if (!resp.empty()) {  // must be in the response
+        last = resp.back();
+    } else {  // else must be in info
+        ASSERT_FALSE(info.empty()) << "'flashing get_unlock_ability' returned empty response";
+        ASSERT_FALSE(info.back().empty()) << "Expected non-empty info response";
+        last = info.back().back();
+    }
+    ASSERT_TRUE(last == '1' || last == '0') << "Unlock ability must report '0' or '1' in response";
+}
+
+TEST_F(Conformance, PartitionInfo) {
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+    EXPECT_GT(parts.size(), 0)
+            << "getvar:all did not report any partition-size: through INFO responses";
+    std::set<std::string> allowed{"ext4", "f2fs", "raw"};
+    for (const auto p : parts) {
+        EXPECT_GE(std::get<1>(p), 0);
+        std::string part(std::get<0>(p));
+        std::set<std::string> allowed{"ext4", "f2fs", "raw"};
+        std::string resp;
+        EXPECT_EQ(fb->GetVar("partition-type:" + part, &resp), SUCCESS);
+        EXPECT_NE(allowed.find(resp), allowed.end()) << "getvar:partition-type:" + part << " was '"
+                                                     << resp << "' this is not a valid type";
+        const std::string cmd = "partition-size:" + part;
+        EXPECT_EQ(fb->GetVar(cmd, &resp), SUCCESS);
+
+        // This must start with 0x
+        EXPECT_FALSE(isspace(resp.front()))
+                << cmd + " responded with a string with leading whitespace";
+        EXPECT_FALSE(resp.compare(0, 2, "0x"))
+                << cmd + "responded with a string that does not start with 0x...";
+        int64_t size = strtoll(resp.c_str(), nullptr, 16);
+        EXPECT_GT(size, 0) << "'" + resp + "' is not a valid response from " + cmd;
+    }
+}
+
+TEST_F(Conformance, Slots) {
+    std::string var;
+    ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "getvar:slot-count failed";
+    ASSERT_EQ(std::count_if(var.begin(), var.end(), isdigit), var.size())
+            << "'" << var << "' is not all digits which it should be for getvar:slot-count";
+    int32_t num_slots = strtol(var.c_str(), nullptr, 10);
+
+    // Can't run out of alphabet letters...
+    ASSERT_LE(num_slots, 26) << "What?! You can't have more than 26 slots";
+
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+
+    std::map<std::string, std::set<char>> part_slots;
+    if (num_slots > 0) {
+        EXPECT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
+
+        for (const auto p : parts) {
+            std::string part(std::get<0>(p));
+            std::regex reg("([[:graph:]]*)_([[:lower:]])");
+            std::smatch sm;
+
+            if (std::regex_match(part, sm, reg)) {  // This partition has slots
+                std::string part_base(sm[1]);
+                std::string slot(sm[2]);
+                EXPECT_EQ(fb->GetVar("has-slot:" + part_base, &var), SUCCESS)
+                        << "'getvar:has-slot:" << part_base << "' failed";
+                EXPECT_EQ(var, "yes") << "'getvar:has-slot:" << part_base << "' was not 'yes'";
+                EXPECT_TRUE(islower(slot.front()))
+                        << "'" << slot.front() << "' is an invalid slot-suffix for " << part_base;
+                std::set<char> tmp{slot.front()};
+                part_slots.emplace(part_base, tmp);
+                part_slots.at(part_base).insert(slot.front());
+            } else {
+                EXPECT_EQ(fb->GetVar("has-slot:" + part, &var), SUCCESS)
+                        << "'getvar:has-slot:" << part << "' failed";
+                EXPECT_EQ(var, "no") << "'getvar:has-slot:" << part << "' should be no";
+            }
+        }
+        // Ensure each partition has the correct slot suffix
+        for (const auto iter : part_slots) {
+            const std::set<char>& char_set = iter.second;
+            std::string chars;
+            for (char c : char_set) {
+                chars += c;
+                chars += ',';
+            }
+            EXPECT_EQ(char_set.size(), num_slots)
+                    << "There should only be slot suffixes from a to " << 'a' + num_slots - 1
+                    << " instead encountered: " << chars;
+            for (const char c : char_set) {
+                EXPECT_GE(c, 'a') << "Encountered invalid slot suffix of '" << c << "'";
+                EXPECT_LT(c, 'a' + num_slots) << "Encountered invalid slot suffix of '" << c << "'";
+            }
+        }
+    }
+}
+
+TEST_F(Conformance, SetActive) {
+    std::string var;
+    ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "getvar:slot-count failed";
+    ASSERT_EQ(std::count_if(var.begin(), var.end(), isdigit), var.size())
+            << "'" << var << "' is not all digits which it should be for getvar:slot-count";
+    int32_t num_slots = strtol(var.c_str(), nullptr, 10);
+
+    // Can't run out of alphabet letters...
+    ASSERT_LE(num_slots, 26) << "You can't have more than 26 slots";
+
+    for (char c = 'a'; c < 'a' + num_slots; c++) {
+        const std::string slot(&c, &c + 1);
+        ASSERT_EQ(fb->SetActive(slot), SUCCESS) << "Set active for slot '" << c << "' failed";
+        ASSERT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
+        EXPECT_EQ(var, slot) << "getvar:current-slot repots incorrect slot after setting it";
+    }
+}
+
+TEST_F(Conformance, LockAndUnlockPrompt) {
+    std::string resp;
+    ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+    ASSERT_TRUE(resp == "yes" || resp == "no")
+            << "Device did not respond with 'yes' or 'no' for getvar:unlocked";
+    bool curr = resp == "yes";
+
+    for (int i = 0; i < 2; i++) {
+        std::string action = !curr ? "unlock" : "lock";
+        printf("Device should prompt to '%s' bootloader, select 'no'\n", action.c_str());
+        SetLockState(!curr, false);
+        ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+        ASSERT_EQ(resp, curr ? "yes" : "no") << "The locked/unlocked state of the bootloader "
+                                                "incorrectly changed after selecting no";
+        printf("Device should prompt to '%s' bootloader, select 'yes'\n", action.c_str());
+        SetLockState(!curr, true);
+        ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
+        ASSERT_EQ(resp, !curr ? "yes" : "no") << "The locked/unlocked state of the bootloader "
+                                                 "failed to change after selecting yes";
+        curr = !curr;
+    }
+}
+
+TEST_F(Conformance, SparseBlockSupport0) {
+    // The sparse block size can be any multiple of 4
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    int64_t size = strtoll(var.c_str(), nullptr, 16);
+
+    // It is reasonable to expect it to handle a single dont care block equal to its DL size
+    for (int64_t bs = 4; bs < size; bs <<= 1) {
+        SparseWrapper sparse(bs, bs);
+        ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
+        EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+        EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
+                << "Download sparse with crc failed: " << sparse.Rep();
+        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    }
+}
+
+TEST_F(Conformance, SparseBlockSupport1) {
+    // The sparse block size can be any multiple of 4
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    int64_t size = strtoll(var.c_str(), nullptr, 16);
+
+    // handle a packed block to half its max download size block
+    for (int64_t bs = 4; bs < size / 2; bs <<= 1) {
+        SparseWrapper sparse(bs, bs);
+        ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
+        std::vector<char> buf = RandomBuf(bs);
+        ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+                << "Adding data failed to sparse file: " << sparse.Rep();
+        EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+        EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
+                << "Download sparse with crc failed: " << sparse.Rep();
+        EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    }
+}
+
+// A single don't care download
+TEST_F(Conformance, SparseDownload0) {
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
+            << "Download sparse with crc failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+}
+
+TEST_F(Conformance, SparseDownload1) {
+    SparseWrapper sparse(4096, 10 * 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 9), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
+            << "Download sparse with crc failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+}
+
+TEST_F(Conformance, SparseDownload2) {
+    SparseWrapper sparse(4096, 4097);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    std::vector<char> buf2 = RandomBuf(1);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 1), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
+            << "Download sparse with crc failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+}
+
+TEST_F(Conformance, SparseDownload3) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    int size = strtoll(var.c_str(), nullptr, 16);
+
+    SparseWrapper sparse(4096, size);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    // Don't want this to take forever
+    unsigned num_chunks = std::min(1000, size / (2 * 4096));
+    for (int i = 0; i < num_chunks; i++) {
+        std::vector<char> buf;
+        int r = random_int(0, 2);
+        // Three cases
+        switch (r) {
+            case 0:
+                break;  // Dont Care chunnk
+            case 1:     // Buffer
+                buf = RandomBuf(4096);
+                ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), i), 0)
+                        << "Adding data failed to sparse file: " << sparse.Rep();
+                break;
+            case 2:  // fill
+                ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, i), 0)
+                        << "Adding fill to sparse file failed: " << sparse.Rep();
+                break;
+        }
+    }
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse, true), SUCCESS)
+            << "Download sparse with crc failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+}
+
+TEST_F(Conformance, SparseVersionCheck) {
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf;
+    ASSERT_TRUE(SparseToBuf(*sparse, &buf)) << "Sparse buffer creation failed";
+    // Invalid, right after magic
+    buf[4] = 0xff;
+    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+
+    // It can either reject this download or reject it during flash
+    if (HandleResponse() != DEVICE_FAIL) {
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing an invalid sparse version should fail " << sparse.Rep();
+    }
+}
+
+TEST_F(Conformance, SparseCRCCheck) {
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    ASSERT_TRUE(SparseToBuf(*sparse, &buf, true)) << "Sparse buffer creation failed";
+    // Flip a bit in the crc
+    buf.back() = buf.back() ^ 0x01;
+    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+    // It can either reject this download or reject it during flash
+    if (HandleResponse() != DEVICE_FAIL) {
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing an invalid sparse version should fail " << sparse.Rep();
+    }
+}
+
+TEST_F(UnlockPermissions, Download) {
+    std::vector<char> buf{'a', 'o', 's', 'p'};
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
+}
+
+TEST_F(UnlockPermissions, DownloadFlash) {
+    std::vector<char> buf{'a', 'o', 's', 'p'};
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in unlocked mode";
+    ;
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in unlocked mode";
+}
+
+TEST_F(LockPermissions, DownloadFlash) {
+    std::vector<char> buf{'a', 'o', 's', 'p'};
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in locked mode";
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in locked mode";
+    std::string resp;
+    for (const auto tup : parts) {
+        EXPECT_EQ(fb->Flash(std::get<0>(tup), &resp), DEVICE_FAIL)
+                << "Device did not respond with FAIL when trying to flash '" << std::get<0>(tup)
+                << "' in locked mode";
+        EXPECT_GT(resp.size(), 0)
+                << "Device sent empty error message after FAIL";  // meaningful error message
+    }
+}
+
+TEST_F(LockPermissions, Erase) {
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+    std::string resp;
+    for (const auto tup : parts) {
+        EXPECT_EQ(fb->Erase(std::get<0>(tup), &resp), DEVICE_FAIL)
+                << "Device did not respond with FAIL when trying to erase '" << std::get<0>(tup)
+                << "' in locked mode";
+        EXPECT_GT(resp.size(), 0) << "Device sent empty error message after FAIL";
+    }
+}
+
+TEST_F(LockPermissions, SetActive) {
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+
+    std::string resp;
+    EXPECT_EQ(fb->GetVar("slot-count", &resp), SUCCESS) << "getvar:slot-count failed";
+    int32_t num_slots = strtol(resp.c_str(), nullptr, 10);
+
+    for (const auto tup : parts) {
+        std::string part(std::get<0>(tup));
+        std::regex reg("([[:graph:]]*)_([[:lower:]])");
+        std::smatch sm;
+
+        if (std::regex_match(part, sm, reg)) {  // This partition has slots
+            std::string part_base(sm[1]);
+            for (char c = 'a'; c < 'a' + num_slots; c++) {
+                // We should not be able to SetActive any of these
+                EXPECT_EQ(fb->SetActive(part_base + '_' + c, &resp), DEVICE_FAIL)
+                        << "set:active:" << part_base + '_' + c << " did not fail in locked mode";
+            }
+        }
+    }
+}
+
+TEST_F(LockPermissions, Boot) {
+    std::vector<char> buf;
+    buf.resize(1000);
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "A 1000 byte download failed";
+    std::string resp;
+    ASSERT_EQ(fb->Boot(&resp), DEVICE_FAIL)
+            << "The device did not respond with failure for 'boot' when locked";
+    EXPECT_GT(resp.size(), 0) << "No error message was returned by device after FAIL";
+}
+
+TEST_F(Fuzz, DownloadSize) {
+    std::string var;
+    EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
+    int64_t size = strtoll(var.c_str(), nullptr, 0);
+    EXPECT_GT(size, 0) << '\'' << var << "' is not a valid response for getvar:max-download-size";
+
+    EXPECT_EQ(DownloadCommand(size + 1), DEVICE_FAIL)
+            << "Device reported max-download-size as '" << size
+            << "' but did not reject a download of " << size + 1;
+
+    std::vector<char> buf(size);
+    EXPECT_EQ(fb->Download(buf), SUCCESS) << "Device reported max-download-size as '" << size
+                                          << "' but downloading a payload of this size failed";
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+}
+
+TEST_F(Fuzz, DownloadPartialBuf) {
+    std::vector<char> buf{'a', 'o', 's', 'p'};
+    ASSERT_EQ(DownloadCommand(buf.size() + 1), SUCCESS)
+            << "Download command for " << buf.size() + 1 << " bytes failed";
+
+    std::string resp;
+    RetCode ret = SendBuffer(buf);
+    EXPECT_EQ(ret, SUCCESS) << "Device did not accept partial payload download";
+    // Send the partial buffer, then cancel it with a reset
+    EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+    // The device better still work after all that if we unplug and replug
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "getvar:product failed";
+}
+
+TEST_F(Fuzz, DownloadOverRun) {
+    std::vector<char> buf(1000, 'F');
+    ASSERT_EQ(DownloadCommand(10), SUCCESS) << "Device rejected download request for 10 bytes";
+    // There are two ways to handle this
+    // Accept download, but send error response
+    // Reject the download outright
+    std::string resp;
+    RetCode ret = SendBuffer(buf);
+    if (ret == SUCCESS) {
+        // If it accepts the buffer, it better send back an error response
+        EXPECT_EQ(HandleResponse(&resp), DEVICE_FAIL)
+                << "After sending too large of a payload for a download command, device accepted "
+                   "payload and did not respond with FAIL";
+    } else {
+        EXPECT_EQ(ret, IO_ERROR) << "After sending too large of a payload for a download command, "
+                                    "device did not return error";
+    }
+
+    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+    // The device better still work after all that if we unplug and replug
+    EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+            << "Device did not respond with SUCCESS to getvar:product.";
+}
+
+TEST_F(Fuzz, DownloadInvalid1) {
+    EXPECT_EQ(DownloadCommand(0), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command 'download:0'";
+}
+
+TEST_F(Fuzz, DownloadInvalid2) {
+    std::string cmd("download:1");
+    EXPECT_EQ(fb->RawCommand("download:1"), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid3) {
+    std::string cmd("download:-1");
+    EXPECT_EQ(fb->RawCommand("download:-1"), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid4) {
+    std::string cmd("download:-01000000");
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid5) {
+    std::string cmd("download:-0100000");
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid6) {
+    std::string cmd("download:");
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid7) {
+    std::string cmd("download:01000000\0999", sizeof("download:01000000\0999"));
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, DownloadInvalid8) {
+    std::string cmd("download:01000000\0dkjfvijafdaiuybgidabgybr",
+                    sizeof("download:01000000\0dkjfvijafdaiuybgidabgybr"));
+    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
+}
+
+TEST_F(Fuzz, GetVarAllSpam) {
+    auto start = std::chrono::high_resolution_clock::now();
+    std::chrono::duration<double> elapsed;
+    unsigned i = 1;
+    do {
+        std::vector<std::string> vars;
+        ASSERT_EQ(fb->GetVarAll(&vars), SUCCESS) << "Device did not respond with success after "
+                                                 << i << "getvar:all commands in a row";
+        ASSERT_GT(vars.size(), 0)
+                << "Device did not send any INFO responses after getvar:all command";
+        elapsed = std::chrono::high_resolution_clock::now() - start;
+    } while (i++, elapsed.count() < 5);
+}
+
+TEST_F(Fuzz, BadCommandTooLarge) {
+    std::string s = RandomString(fastboot::FB_COMMAND_SZ + 1, rand_legal);
+    EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s.size()
+            << " string of random ASCII chars";
+    std::string s1 = RandomString(1000, rand_legal);
+    EXPECT_EQ(fb->RawCommand(s1), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s1.size()
+            << " string of random ASCII chars";
+    std::string s2 = RandomString(1000, rand_illegal);
+    EXPECT_EQ(fb->RawCommand(s2), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s1.size()
+            << " string of random non-ASCII chars";
+    std::string s3 = RandomString(1000, rand_char);
+    EXPECT_EQ(fb->RawCommand(s3), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s1.size()
+            << " string of random chars";
+}
+
+TEST_F(Fuzz, CommandTooLarge) {
+    for (const std::string& s : CMDS) {
+        std::string rs = RandomString(1000, rand_char);
+        EXPECT_EQ(fb->RawCommand(s + rs), DEVICE_FAIL)
+                << "Device did not respond with failure after '" << s + rs << "'";
+        ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
+        std::string resp;
+        EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+                << "Device is unresponsive to getvar command";
+    }
+}
+
+TEST_F(Fuzz, CommandMissingArgs) {
+    for (const std::string& s : CMDS) {
+        if (s.back() == ':') {
+            EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
+                    << "Device did not respond with failure after '" << s << "'";
+            std::string sub(s.begin(), s.end() - 1);
+            EXPECT_EQ(fb->RawCommand(sub), DEVICE_FAIL)
+                    << "Device did not respond with failure after '" << sub << "'";
+        } else {
+            std::string rs = RandomString(10, rand_illegal);
+            EXPECT_EQ(fb->RawCommand(rs + s), DEVICE_FAIL)
+                    << "Device did not respond with failure after '" << rs + s << "'";
+        }
+        std::string resp;
+        EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+                << "Device is unresponsive to getvar command";
+    }
+}
+
+TEST_F(Fuzz, SparseZeroLength) {
+    SparseWrapper sparse(4096, 0);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    RetCode ret = fb->Download(*sparse);
+    // Two ways to handle it
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing zero length sparse image did not fail: " << sparse.Rep();
+    }
+    ret = fb->Download(*sparse, true);
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing zero length sparse image did not fail " << sparse.Rep();
+    }
+}
+
+TEST_F(Fuzz, SparseTooManyChunks) {
+    SparseWrapper sparse(4096, 4096);  // 1 block, but we send two chunks that will use 2 blocks
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    // We take advantage of the fact the sparse library does not check this
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, 1), 0)
+            << "Adding fill to sparse file failed: " << sparse.Rep();
+
+    RetCode ret = fb->Download(*sparse);
+    // Two ways to handle it
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
+                << sparse.Rep();
+    }
+    ret = fb->Download(*sparse, true);
+    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash
+        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+                << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
+                << sparse.Rep();
+    }
+}
+
+TEST_F(Fuzz, USBResetSpam) {
+    auto start = std::chrono::high_resolution_clock::now();
+    std::chrono::duration<double> elapsed;
+    int i = 0;
+    do {
+        ASSERT_EQ(transport->Reset(), 0) << "USB Reset failed after " << i << " resets in a row";
+        elapsed = std::chrono::high_resolution_clock::now() - start;
+    } while (i++, elapsed.count() < 5);
+    std::string resp;
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
+            << "getvar failed after " << i << " USB reset(s) in a row";
+}
+
+TEST_F(Fuzz, USBResetCommandSpam) {
+    auto start = std::chrono::high_resolution_clock::now();
+    std::chrono::duration<double> elapsed;
+    do {
+        std::string resp;
+        std::vector<std::string> all;
+        ASSERT_EQ(transport->Reset(), 0) << "USB Reset failed";
+        EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << "getvar:all failed after USB reset";
+        EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "getvar:product failed";
+        elapsed = std::chrono::high_resolution_clock::now() - start;
+    } while (elapsed.count() < 10);
+}
+
+TEST_F(Fuzz, USBResetAfterDownload) {
+    std::vector<char> buf;
+    buf.resize(1000000);
+    EXPECT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Download command failed";
+    EXPECT_EQ(transport->Reset(), 0) << "USB Reset failed";
+    std::vector<std::string> all;
+    EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << "getvar:all failed after USB reset.";
+}
+
+// Getvar XML tests
+TEST_P(ExtensionsGetVarConformance, VarExists) {
+    std::string resp;
+    EXPECT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);
+}
+
+TEST_P(ExtensionsGetVarConformance, VarMatchesRegex) {
+    std::string resp;
+    ASSERT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);
+    std::smatch sm;
+    std::regex_match(resp, sm, GetParam().second.regex);
+    EXPECT_FALSE(sm.empty()) << "The regex did not match";
+}
+
+INSTANTIATE_TEST_CASE_P(XMLGetVar, ExtensionsGetVarConformance,
+                        ::testing::ValuesIn(GETVAR_XML_TESTS));
+
+TEST_P(AnyPartition, ReportedGetVarAll) {
+    // As long as the partition is reported in INFO, it would be tested by generic Conformance
+    std::vector<std::tuple<std::string, uint64_t>> parts;
+    ASSERT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
+    const std::string name = GetParam().first;
+    if (GetParam().second.slots) {
+        auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {
+            return std::get<0>(tup) == name + "_a";
+        };
+        EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())
+                << "partition '" + name + "_a' not reported in getvar:all";
+    } else {
+        auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {
+            return std::get<0>(tup) == name;
+        };
+        EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())
+                << "partition '" + name + "' not reported in getvar:all";
+    }
+}
+
+TEST_P(AnyPartition, Hashable) {
+    const std::string name = GetParam().first;
+    if (!config.checksum.empty()) {  // We can use hash to validate
+        for (const auto& part_name : real_parts) {
+            // Get hash
+            std::string hash;
+            int retcode;
+            std::string err_msg;
+            if (GetParam().second.hashable) {
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                EXPECT_EQ(retcode, 0) << err_msg;
+            } else {  // Make sure it fails
+                const std::string cmd = config.checksum + ' ' + part_name;
+                EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
+                        << part_name + " is marked as non-hashable, but hashing did not fail";
+            }
+        }
+    }
+}
+
+TEST_P(WriteablePartition, FlashCheck) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf = RandomBuf(max_flash, rand_char);
+        EXPECT_EQ(fb->FlashPartition(part_name, buf), part_info.parsed ? DEVICE_FAIL : SUCCESS)
+                << "A partition with an image parsed by the bootloader should reject random "
+                   "garbage "
+                   "otherwise it should succeed";
+    }
+}
+
+TEST_P(WriteablePartition, EraseCheck) {
+    const std::string name = GetParam().first;
+
+    for (const auto& part_name : real_parts) {
+        ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+    }
+}
+
+TEST_P(WriteHashNonParsedPartition, EraseZerosData) {
+    const std::string name = GetParam().first;
+
+    for (const auto& part_name : real_parts) {
+        std::string err_msg;
+        int retcode;
+        const std::vector<char> buf = RandomBuf(max_flash, rand_char);
+        // Partition is too big to write to entire thing
+        // This can eventually be supported by using sparse images if too large
+        if (max_flash < part_size) {
+            std::string hash_before, hash_after;
+            ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_NE(hash_before, hash_after)
+                    << "The partition hash for " + part_name +
+                               " did not change after erasing a known value";
+        } else {
+            std::string hash_zeros, hash_ones, hash_middle, hash_after;
+            const std::vector<char> buf_zeros(max_flash, 0);
+            const std::vector<char> buf_ones(max_flash, -1);  // All bits are set to 1
+            ASSERT_EQ(fb->FlashPartition(part_name, buf_zeros), SUCCESS);
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_zeros, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            ASSERT_EQ(fb->FlashPartition(part_name, buf_ones), SUCCESS);
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_ones, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            ASSERT_NE(hash_zeros, hash_ones)
+                    << "Hashes of partion should not be the same when all bytes are 0xFF or 0x00";
+            ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_middle, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            ASSERT_NE(hash_zeros, hash_middle)
+                    << "Hashes of partion are the same when all bytes are 0x00 or test payload";
+            ASSERT_NE(hash_ones, hash_middle)
+                    << "Hashes of partion are the same when all bytes are 0xFF or test payload";
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_TRUE(hash_zeros == hash_after || hash_ones == hash_after)
+                    << "Erasing " + part_name + " should set all the bytes to 0xFF or 0x00";
+        }
+    }
+}
+
+// Only partitions that we can write and hash (name, fixture), TEST_P is (Fixture, test_name)
+INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashNonParsed, WriteHashNonParsedPartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITE_HASH_NONPARSED));
+
+INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashable, WriteHashablePartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITE_HASHABLE));
+
+// only partitions writeable
+INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteable, WriteablePartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITEABLE));
+
+// Every partition
+INSTANTIATE_TEST_CASE_P(XMLPartitionsAll, AnyPartition, ::testing::ValuesIn(PARTITION_XML_TESTS));
+
+// Partition Fuzz tests
+TEST_P(FuzzWriteablePartition, BoundsCheck) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        // try and flash +1 too large, first erase and get a hash, make sure it does not change
+        std::vector<char> buf = RandomBuf(max_flash + 1);  // One too large
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "Flashing an image 1 byte too large to " + part_name + " did not fail";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "Flashing too large of an image resulted in a changed partition hash for " +
+                               part_name;
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "Flashing an image 1 byte too large to " + part_name + " did not fail";
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteable, FuzzWriteablePartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITEABLE));
+
+// A parsed partition should have magic and such that is checked by the bootloader
+// Attempting to flash a random single byte should definately fail
+TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageSmall) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf = RandomBuf(1);
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should fail on a single byte";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "Flashing a single byte to parsed partition  " + part_name +
+                               " should fail and not change the partition hash";
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "Flashing a 1 byte image to a parsed partition should fail";
+        }
+    }
+}
+
+TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf = RandomBuf(max_flash);
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept randomly generated images";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "The hash of the partition has changed after attempting to flash garbage to "
+                       "a parsed partition";
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept randomly generated images";
+        }
+    }
+}
+
+TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge2) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf(max_flash, -1);  // All 1's
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept a image of all 0xFF";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "The hash of the partition has changed after attempting to flash garbage to "
+                       "a parsed partition";
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept a image of all 0xFF";
+        }
+    }
+}
+
+TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge3) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+
+    for (const auto& part_name : real_parts) {
+        std::vector<char> buf(max_flash, 0);  // All 0's
+        if (part_info.hashable) {
+            std::string hash_before, hash_after, err_msg;
+            int retcode;
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept a image of all 0x00";
+            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
+                    << err_msg;
+            ASSERT_EQ(retcode, 0) << err_msg;
+            EXPECT_EQ(hash_before, hash_after)
+                    << "The hash of the partition has changed after attempting to flash garbage to "
+                       "a parsed partition";
+        } else {
+            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                    << "A parsed partition should not accept a image of all 0x00";
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteableParsed, FuzzWriteableParsedPartition,
+                        ::testing::ValuesIn(PARTITION_XML_WRITE_PARSED));
+
+// Make sure all attempts to flash things are rejected
+TEST_P(FuzzAnyPartitionLocked, RejectFlash) {
+    std::vector<char> buf = RandomBuf(5);
+    for (const auto& part_name : real_parts) {
+        ASSERT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
+                << "Flashing a partition should always fail in locked mode";
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLFuzzAnyPartitionLocked, FuzzAnyPartitionLocked,
+                        ::testing::ValuesIn(PARTITION_XML_TESTS));
+
+// Test flashing unlock erases userdata
+TEST_P(UserdataPartition, UnlockErases) {
+    // Get hash after an erase
+    int retcode;
+    std::string err_msg, hash_before, hash_buf, hash_after;
+    ASSERT_EQ(fb->Erase("userdata"), SUCCESS) << "Erasing uesrdata failed";
+    ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_before, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    // Write garbage
+    std::vector<char> buf = RandomBuf(max_flash / 2);
+    ASSERT_EQ(fb->FlashPartition("userdata", buf), SUCCESS);
+    ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_buf, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    // Sanity check of hash
+    EXPECT_NE(hash_before, hash_buf)
+            << "Writing a random buffer to 'userdata' had the same hash as after erasing it";
+    SetLockState(true);  // Lock the device
+
+    SetLockState(false);  // Unlock the device (should cause erase)
+    ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_after, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_NE(hash_after, hash_buf) << "Unlocking the device did not cause the hash of userdata to "
+                                       "change (i.e. it was not erased as required)";
+    EXPECT_EQ(hash_after, hash_before) << "Unlocking the device did not produce the same hash of "
+                                          "userdata as after doing an erase to userdata";
+}
+
+// This is a hack to make this test disapeer if there is not a checsum, userdata is not hashable,
+// or userdata is not marked to be writeable in testing
+INSTANTIATE_TEST_CASE_P(XMLUserdataLocked, UserdataPartition,
+                        ::testing::ValuesIn(PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE));
+
+// Packed images test
+TEST_P(ExtensionsPackedValid, TestDeviceUnpack) {
+    const std::string& packed_name = GetParam().first;
+    const std::string& packed_image = GetParam().second.packed_img;
+    const std::string& unpacked = GetParam().second.unpacked_dir;
+
+    // First we need to check for existence of images
+    const extension::Configuration::PackedInfo& info = config.packed[packed_name];
+
+    const auto flash_part = [&](const std::string fname, const std::string part_name) {
+        FILE* to_flash = fopen((SEARCH_PATH + fname).c_str(), "rb");
+        ASSERT_NE(to_flash, nullptr) << "'" << fname << "'"
+                                     << " failed to open for flashing";
+        int fd = fileno(to_flash);
+        size_t fsize = lseek(fd, 0, SEEK_END);
+        ASSERT_GT(fsize, 0) << fname + " appears to be an empty image";
+        ASSERT_EQ(fb->FlashPartition(part_name, fd, fsize), SUCCESS);
+        fclose(to_flash);
+    };
+
+    // We first need to set the slot count
+    std::string var;
+    int num_slots = 1;
+    if (info.slots) {
+        ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
+        num_slots = strtol(var.c_str(), nullptr, 10);
+    } else {
+        for (const auto& part : info.children) {
+            EXPECT_FALSE(config.partitions[part].slots)
+                    << "A partition can not have slots if the packed image does not";
+        }
+    }
+
+    for (int i = 0; i < num_slots; i++) {
+        std::unordered_map<std::string, std::string> initial_hashes;
+        const std::string packed_suffix =
+                info.slots ? android::base::StringPrintf("_%c", 'a' + i) : "";
+
+        // Flash the paritions manually and get hash
+        for (const auto& part : info.children) {
+            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
+            const std::string suffix = part_info.slots ? packed_suffix : "";
+            const std::string part_name = part + suffix;
+
+            ASSERT_EQ(fb->Erase(part_name), SUCCESS);
+            const std::string fpath = unpacked + '/' + part + ".img";
+            ASSERT_NO_FATAL_FAILURE(flash_part(fpath, part_name))
+                    << "Failed to flash '" + fpath + "'";
+            // If the partition is hashable we store it
+            if (part_info.hashable) {
+                std::string hash, err_msg;
+                int retcode;
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                ASSERT_EQ(retcode, 0) << err_msg;
+                initial_hashes[part] = hash;
+            }
+        }
+
+        // erase once at the end, to avoid false positives if flashing does nothing
+        for (const auto& part : info.children) {
+            const std::string suffix = config.partitions[part].slots ? packed_suffix : "";
+            ASSERT_EQ(fb->Erase(part + suffix), SUCCESS);
+        }
+
+        // Now we flash the packed image and compare our hashes
+        ASSERT_NO_FATAL_FAILURE(flash_part(packed_image, packed_name + packed_suffix));
+
+        for (const auto& part : info.children) {
+            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
+            // If the partition is hashable we check it
+            if (part_info.hashable) {
+                const std::string suffix = part_info.slots ? packed_suffix : "";
+                const std::string part_name = part + suffix;
+                std::string hash, err_msg;
+                int retcode;
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                ASSERT_EQ(retcode, 0) << err_msg;
+                std::string msg =
+                        "The hashes between flashing the packed image and directly flashing '" +
+                        part_name + "' does not match";
+                EXPECT_EQ(hash, initial_hashes[part]) << msg;
+            }
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedValid,
+                        ::testing::ValuesIn(PACKED_XML_SUCCESS_TESTS));
+
+// Packed images test
+TEST_P(ExtensionsPackedInvalid, TestDeviceUnpack) {
+    const std::string& packed_name = GetParam().first;
+    const std::string& packed_image = GetParam().second.packed_img;
+
+    // First we need to check for existence of images
+    const extension::Configuration::PackedInfo& info = config.packed[packed_name];
+
+    // We first need to set the slot count
+    std::string var;
+    int num_slots = 1;
+    if (info.slots) {
+        ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
+        num_slots = strtol(var.c_str(), nullptr, 10);
+    } else {
+        for (const auto& part : info.children) {
+            EXPECT_FALSE(config.partitions[part].slots)
+                    << "A partition can not have slots if the packed image does not";
+        }
+    }
+
+    for (int i = 0; i < num_slots; i++) {
+        std::unordered_map<std::string, std::string> initial_hashes;
+        const std::string packed_suffix =
+                info.slots ? android::base::StringPrintf("_%c", 'a' + i) : "";
+
+        // manually and get hash
+        for (const auto& part : info.children) {
+            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
+            const std::string suffix = part_info.slots ? packed_suffix : "";
+            const std::string part_name = part + suffix;
+
+            // If the partition is hashable we store it
+            if (part_info.hashable) {
+                std::string hash, err_msg;
+                int retcode;
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                ASSERT_EQ(retcode, 0) << err_msg;
+                initial_hashes[part] = hash;
+            }
+        }
+
+        // Attempt to flash the invalid file
+        FILE* to_flash = fopen((SEARCH_PATH + packed_image).c_str(), "rb");
+        ASSERT_NE(to_flash, nullptr) << "'" << packed_image << "'"
+                                     << " failed to open for flashing";
+        int fd = fileno(to_flash);
+        size_t fsize = lseek(fd, 0, SEEK_END);
+        ASSERT_GT(fsize, 0) << packed_image + " appears to be an empty image";
+        ASSERT_EQ(fb->FlashPartition(packed_name + packed_suffix, fd, fsize), DEVICE_FAIL)
+                << "Expected flashing to fail for " + packed_image;
+        fclose(to_flash);
+
+        for (const auto& part : info.children) {
+            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
+            // If the partition is hashable we check it
+            if (part_info.hashable) {
+                const std::string suffix = part_info.slots ? packed_suffix : "";
+                const std::string part_name = part + suffix;
+                std::string hash, err_msg;
+                int retcode;
+                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
+                        << err_msg;
+                ASSERT_EQ(retcode, 0) << err_msg;
+                std::string msg = "Flashing an invalid image changed the hash of '" + part_name;
+                EXPECT_EQ(hash, initial_hashes[part]) << msg;
+            }
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedInvalid,
+                        ::testing::ValuesIn(PACKED_XML_FAIL_TESTS));
+
+// OEM xml tests
+TEST_P(ExtensionsOemConformance, RunOEMTest) {
+    const std::string& cmd = std::get<0>(GetParam());
+    // bool restricted = std::get<1>(GetParam());
+    const extension::Configuration::CommandTest& test = std::get<2>(GetParam());
+
+    const RetCode expect = (test.expect == extension::FAIL) ? DEVICE_FAIL : SUCCESS;
+
+    // Does the test require staging something?
+    if (!test.input.empty()) {  // Non-empty string
+        FILE* to_stage = fopen((SEARCH_PATH + test.input).c_str(), "rb");
+        ASSERT_NE(to_stage, nullptr) << "'" << test.input << "'"
+                                     << " failed to open for staging";
+        int fd = fileno(to_stage);
+        size_t fsize = lseek(fd, 0, SEEK_END);
+        std::string var;
+        EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS);
+        int64_t size = strtoll(var.c_str(), nullptr, 16);
+        EXPECT_LT(fsize, size) << "'" << test.input << "'"
+                               << " is too large for staging";
+        ASSERT_EQ(fb->Download(fd, fsize), SUCCESS) << "'" << test.input << "'"
+                                                    << " failed to download for staging";
+        fclose(to_stage);
+    }
+    // Run the command
+    int dsize = -1;
+    std::string resp;
+    const std::string full_cmd = "oem " + cmd + " " + test.arg;
+    ASSERT_EQ(fb->RawCommand(full_cmd, &resp, nullptr, &dsize), expect);
+
+    // This is how we test if indeed data response
+    if (test.expect == extension::DATA) {
+        EXPECT_GT(dsize, 0);
+    }
+
+    // Validate response if neccesary
+    if (!test.regex_str.empty()) {
+        std::smatch sm;
+        std::regex_match(resp, sm, test.regex);
+        EXPECT_FALSE(sm.empty()) << "The oem regex did not match";
+    }
+
+    // If payload, we validate that as well
+    const std::vector<std::string> args = SplitBySpace(test.validator);
+    if (args.size()) {
+        // Save output
+        const std::string save_loc =
+                OUTPUT_PATH + (test.output.empty() ? DEFAULT_OUPUT_NAME : test.output);
+        std::string resp;
+        ASSERT_EQ(fb->Upload(save_loc, &resp), SUCCESS)
+                << "Saving output file failed with (" << fb->Error() << ") " << resp;
+        // Build the arguments to the validator
+        std::vector<std::string> prog_args(args.begin() + 1, args.end());
+        prog_args.push_back(full_cmd);  // Pass in the full command
+        prog_args.push_back(save_loc);  // Pass in the save location
+        // Run the validation program
+        int pipe;
+        const pid_t pid = StartProgram(args[0], prog_args, &pipe);
+        ASSERT_GT(pid, 0) << "Failed to launch validation program: " << args[0];
+        std::string error_msg;
+        int ret = WaitProgram(pid, pipe, &error_msg);
+        EXPECT_EQ(ret, 0) << error_msg;  // Program exited correctly
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(XMLOEM, ExtensionsOemConformance, ::testing::ValuesIn(OEM_XML_TESTS));
+
+// Sparse Tests
+TEST_P(SparseTestPartition, SparseSingleBlock) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    SparseWrapper sparse(4096, 4096);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(4096);
+    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+TEST_P(SparseTestPartition, SparseFill) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    int64_t size = (max_dl / 4096) * 4096;
+    SparseWrapper sparse(4096, size);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size, 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    std::vector<char> buf(size);
+    for (auto iter = buf.begin(); iter < buf.end(); iter += 4) {
+        iter[0] = 0xef;
+        iter[1] = 0xbe;
+        iter[2] = 0xad;
+        iter[3] = 0xde;
+    }
+    EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+// This tests to make sure it does not overwrite previous flashes
+TEST_P(SparseTestPartition, SparseMultiple) {
+    const std::string name = GetParam().first;
+    auto part_info = GetParam().second;
+    const std::string part_name = name + (part_info.slots ? "_a" : "");
+    int64_t size = (max_dl / 4096) * 4096;
+    SparseWrapper sparse(4096, size / 2);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size / 2, 0), 0)
+            << "Adding data failed to sparse file: " << sparse.Rep();
+    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+
+    SparseWrapper sparse2(4096, size / 2);
+    ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+    std::vector<char> buf = RandomBuf(size / 2);
+    ASSERT_EQ(sparse_file_add_data(*sparse2, buf.data(), buf.size(), (size / 2) / 4096), 0)
+            << "Adding data failed to sparse file: " << sparse2.Rep();
+    EXPECT_EQ(fb->Download(*sparse2), SUCCESS) << "Download sparse failed: " << sparse2.Rep();
+    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse2.Rep();
+
+    std::string hash, hash_new, err_msg;
+    int retcode;
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+    // Now flash it the non-sparse way
+    std::vector<char> fbuf(size);
+    for (auto iter = fbuf.begin(); iter < fbuf.begin() + size / 2; iter += 4) {
+        iter[0] = 0xef;
+        iter[1] = 0xbe;
+        iter[2] = 0xad;
+        iter[3] = 0xde;
+    }
+    fbuf.assign(buf.begin(), buf.end());
+    EXPECT_EQ(fb->FlashPartition(part_name, fbuf), SUCCESS) << "Flashing image failed: ";
+    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+    ASSERT_EQ(retcode, 0) << err_msg;
+
+    EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+                                 "methods did not result in the same hash";
+}
+
+INSTANTIATE_TEST_CASE_P(XMLSparseTest, SparseTestPartition,
+                        ::testing::ValuesIn(SINGLE_PARTITION_XML_WRITE_HASHABLE));
+
+void GenerateXmlTests(const extension::Configuration& config) {
+    // Build the getvar tests
+    for (const auto it : config.getvars) {
+        GETVAR_XML_TESTS.push_back(std::make_pair(it.first, it.second));
+    }
+
+    // Build the partition tests, to interface with gtest we need to do it this way
+    for (const auto it : config.partitions) {
+        const auto tup = std::make_tuple(it.first, it.second);
+        PARTITION_XML_TESTS.push_back(tup);  // All partitions
+
+        if (it.second.test == it.second.YES) {
+            PARTITION_XML_WRITEABLE.push_back(tup);  // All writeable partitions
+
+            if (it.second.hashable) {
+                PARTITION_XML_WRITE_HASHABLE.push_back(tup);  // All write and hashable
+                if (!it.second.parsed) {
+                    PARTITION_XML_WRITE_HASH_NONPARSED.push_back(
+                            tup);  // All write hashed and non-parsed
+                }
+            }
+            if (it.second.parsed) {
+                PARTITION_XML_WRITE_PARSED.push_back(tup);  // All write and parsed
+            }
+        }
+    }
+
+    // Build the packed tests, only useful if we have a hash
+    if (!config.checksum.empty()) {
+        for (const auto it : config.packed) {
+            for (const auto& test : it.second.tests) {
+                const auto tup = std::make_tuple(it.first, test);
+                if (test.expect == extension::OKAY) {  // only testing the success case
+                    PACKED_XML_SUCCESS_TESTS.push_back(tup);
+                } else {
+                    PACKED_XML_FAIL_TESTS.push_back(tup);
+                }
+            }
+        }
+    }
+
+    // This is a hack to make this test disapeer if there is not a checksum, userdata is not
+    // hashable, or userdata is not marked to be writeable in testing
+    const auto part_info = config.partitions.find("userdata");
+    if (!config.checksum.empty() && part_info != config.partitions.end() &&
+        part_info->second.hashable &&
+        part_info->second.test == extension::Configuration::PartitionInfo::YES) {
+        PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE.push_back(
+                std::make_tuple(part_info->first, part_info->second));
+    }
+
+    if (!PARTITION_XML_WRITE_HASHABLE.empty()) {
+        SINGLE_PARTITION_XML_WRITE_HASHABLE.push_back(PARTITION_XML_WRITE_HASHABLE.front());
+    }
+
+    // Build oem tests
+    for (const auto it : config.oem) {
+        auto oem_cmd = it.second;
+        for (const auto& t : oem_cmd.tests) {
+            OEM_XML_TESTS.push_back(std::make_tuple(it.first, oem_cmd.restricted, t));
+        }
+    }
+}
+
+}  // namespace fastboot
+
+int main(int argc, char** argv) {
+    std::string err;
+    // Parse the args
+    const std::unordered_map<std::string, std::string> args = fastboot::ParseArgs(argc, argv, &err);
+    if (!err.empty()) {
+        printf("%s\n", err.c_str());
+        return -1;
+    }
+
+    if (args.find("config") != args.end()) {
+        auto found = args.find("search_path");
+        fastboot::SEARCH_PATH = (found != args.end()) ? found->second + "/" : "";
+        found = args.find("output_path");
+        fastboot::OUTPUT_PATH = (found != args.end()) ? found->second + "/" : "/tmp/";
+        if (!fastboot::extension::ParseXml(fastboot::SEARCH_PATH + args.at("config"),
+                                           &fastboot::config)) {
+            printf("XML config parsing failed\n");
+            return -1;
+        }
+        // To interface with gtest, must set global scope test variables
+        fastboot::GenerateXmlTests(fastboot::config);
+    }
+
+    setbuf(stdout, NULL);  // no buffering
+    printf("<Waiting for Device>\n");
+    const auto matcher = [](usb_ifc_info* info) -> int {
+        return fastboot::FastBootTest::MatchFastboot(info, nullptr);
+    };
+    Transport* transport = nullptr;
+    while (!transport) {
+        transport = usb_open(matcher);
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    }
+    transport->Close();
+
+    if (args.find("serial_port") != args.end()) {
+        fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at("serial_port"));
+    }
+
+    ::testing::InitGoogleTest(&argc, argv);
+    auto ret = RUN_ALL_TESTS();
+    if (fastboot::FastBootTest::serial_port > 0) {
+        close(fastboot::FastBootTest::serial_port);
+    }
+    return ret;
+}
diff --git a/fastboot/fuzzy_fastboot/test_listeners.h b/fastboot/fuzzy_fastboot/test_listeners.h
new file mode 100644
index 0000000..ae5b0cb
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/test_listeners.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#pragma once
+
+// TODO, print out logs for each failed test with custom listner
+
+class MinimalistPrinter : public ::testing::EmptyTestEventListener {
+    // Called before a test starts.
+    virtual void OnTestStart(const ::testing::TestInfo& test_info) {
+        printf("*** Test %s.%s starting.\n", test_info.test_case_name(), test_info.name());
+    }
+
+    // Called after a failed assertion or a SUCCESS().
+    virtual void OnTestPartResult(const ::testing::TestPartResult& test_part_result) {
+        printf("%s in %s:%d\n%s\n", test_part_result.failed() ? "*** Failure" : "Success",
+               test_part_result.file_name(), test_part_result.line_number(),
+               test_part_result.summary());
+    }
+
+    // Called after a test ends.
+    virtual void OnTestEnd(const ::testing::TestInfo& test_info) {
+        printf("*** Test %s.%s ending.\n", test_info.test_case_name(), test_info.name());
+    }
+};
diff --git a/fastboot/fuzzy_fastboot/test_utils.cpp b/fastboot/fuzzy_fastboot/test_utils.cpp
new file mode 100644
index 0000000..9ad98be
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/test_utils.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "test_utils.h"
+#include <fcntl.h>
+#include <termios.h>
+#include <sstream>
+
+namespace fastboot {
+
+namespace {
+constexpr int rand_seed = 0;
+std::default_random_engine rnd(rand_seed);
+}  // namespace
+
+char rand_legal() {
+    return rnd() % 128;
+}
+
+char rand_illegal() {
+    return rand_legal() + 128;
+}
+
+char rand_char() {
+    return rnd() % 256;
+}
+
+int random_int(int start, int end) {
+    std::uniform_int_distribution<int> uni(start, end);
+    return uni(rnd);
+}
+
+std::string RandomString(size_t length, std::function<char(void)> provider) {
+    std::string str(length, 0);
+    std::generate_n(str.begin(), length, provider);
+    return str;
+}
+
+std::vector<char> RandomBuf(size_t length, std::function<char(void)> provider) {
+    std::vector<char> ret;
+    ret.resize(length);
+    std::generate_n(ret.begin(), length, provider);
+    return ret;
+}
+
+std::vector<std::string> SplitBySpace(const std::string& s) {
+    std::istringstream iss(s);
+    return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+                                    std::istream_iterator<std::string>{}};
+}
+
+std::vector<std::string> GeneratePartitionNames(const std::string& base, int num_slots) {
+    if (!num_slots) {
+        return std::vector<std::string>{base};
+    }
+    std::vector<std::string> ret;
+    for (char c = 'a'; c < 'a' + num_slots; c++) {
+        ret.push_back(base + '_' + c);
+    }
+
+    return ret;
+}
+
+std::unordered_map<std::string, std::string> ParseArgs(int argc, char** argv,
+                                                       std::string* err_msg) {
+    // We ignore any gtest stuff
+    std::unordered_map<std::string, std::string> ret;
+
+    for (int i = 1; i < argc - 1; i++) {
+        std::string arg(argv[i]);
+
+        const std::string gtest_start("--gtest");
+        // We found a non gtest argument
+        if (!arg.find("-h") ||
+            (!arg.find("--") && arg.find("--gtest") && arg.find("=") != arg.npos)) {
+            const std::string start(arg.begin() + 2, arg.begin() + arg.find("="));
+            const std::string end(arg.begin() + arg.find("=") + 1, arg.end());
+            ret[start] = end;
+        } else if (arg.find("--gtest") != 0) {
+            *err_msg = android::base::StringPrintf("Illegal argument '%s'\n", arg.c_str());
+            return ret;
+        }
+    }
+
+    return ret;
+}
+
+int ConfigureSerial(const std::string& port) {
+    int fd = open(port.c_str(), O_RDONLY | O_NOCTTY | O_NONBLOCK);
+
+    if (fd <= 0) {
+        return fd;
+    }
+
+    struct termios tty;
+    tcgetattr(fd, &tty);
+
+    cfsetospeed(&tty, (speed_t)B115200);
+    cfsetispeed(&tty, (speed_t)B115200);
+
+    tty.c_cflag &= ~PARENB;
+    tty.c_cflag &= ~CSTOPB;
+    tty.c_cflag &= ~CSIZE;
+    tty.c_cflag |= CS8;
+
+    tty.c_cflag &= ~CRTSCTS;
+    tty.c_cc[VMIN] = 0;
+    tty.c_cc[VTIME] = 2;
+    tty.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+
+    cfmakeraw(&tty);
+
+    tcflush(fd, TCIFLUSH);
+    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
+        return -1;
+    }
+
+    return fd;
+}
+
+int StartProgram(const std::string program, const std::vector<std::string> args, int* rpipe) {
+    int link[2];
+    if (pipe(link) < 0) {
+        return -1;
+    }
+
+    pid_t pid = fork();
+    if (pid < 0) {  // error
+        return -1;
+    }
+
+    if (pid) {  // parent
+        close(link[1]);
+        *rpipe = link[0];
+        fcntl(*rpipe, F_SETFL, O_NONBLOCK);  // Non-blocking
+    } else {                                 // child
+        std::vector<const char*> argv(args.size() + 2, nullptr);
+        argv[0] = program.c_str();
+
+        for (int i = 0; i < args.size(); i++) {
+            argv[i + 1] = args[i].c_str();
+        }
+
+        // We pipe any stderr writes to the parent test process
+        dup2(link[1], STDERR_FILENO);  // close stdout and have it now be link[1]
+        // Close duplicates
+        close(link[0]);
+        close(link[1]);
+
+        execvp(program.c_str(), const_cast<char* const*>(argv.data()));
+        fprintf(stderr, "Launching validator process '%s' failed with: %s\n", program.c_str(),
+                strerror(errno));
+        exit(-1);
+    }
+
+    return pid;
+}
+
+int WaitProgram(const int pid, const int pipe, std::string* error_msg) {
+    int status;
+    if (waitpid(pid, &status, 0) != pid) {
+        close(pipe);
+        return -1;
+    }
+    // Read from pipe
+    char buf[1024];
+    int n;
+    while ((n = read(pipe, buf, sizeof(buf))) > 0) {
+        buf[n] = 0; /* terminate the string */
+        error_msg->append(buf, n);
+    }
+    close(pipe);
+
+    if (WIFEXITED(status)) {
+        // This WEXITSTATUS macro masks off lower bytes, with no sign extension
+        // casting it as a signed char fixes the sign extension issue
+        int retmask = WEXITSTATUS(status);
+        return reinterpret_cast<int8_t*>(&retmask)[0];
+    }
+
+    return -1;
+}
+
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/test_utils.h b/fastboot/fuzzy_fastboot/test_utils.h
new file mode 100644
index 0000000..0b032e4
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/test_utils.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#pragma once
+
+#include <sparse/sparse.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <cstdlib>
+#include <fstream>
+#include <random>
+#include <string>
+#include <unordered_map>
+#include "fastboot_driver.h"
+
+namespace fastboot {
+
+char rand_legal();
+char rand_illegal();
+char rand_char();
+// start and end are inclusive
+int random_int(int start, int end);
+
+// I don't want to have to manage memory for this guy
+struct SparseWrapper {
+    SparseWrapper(unsigned int block_size, int64_t len) {
+        sparse = sparse_file_new(block_size, len);
+    }
+
+    SparseWrapper(struct sparse_file* sf) { sparse = sf; }
+
+    ~SparseWrapper() {
+        if (sparse) {
+            sparse_file_destroy(sparse);
+        }
+    }
+
+    const std::string Rep() {
+        unsigned bs = sparse_file_block_size(sparse);
+        unsigned len = sparse_file_len(sparse, true, false);
+        return android::base::StringPrintf("[block_size=%u, len=%u]", bs, len);
+    }
+
+    struct sparse_file* operator*() {
+        return sparse;
+    }
+
+    struct sparse_file* sparse;
+};
+
+std::string RandomString(size_t length, std::function<char(void)> provider);
+// RVO will avoid copy
+std::vector<char> RandomBuf(size_t length, std::function<char(void)> provider = rand_char);
+
+std::vector<std::string> SplitBySpace(const std::string& s);
+
+std::unordered_map<std::string, std::string> ParseArgs(int argc, char** argv, std::string* err_msg);
+
+std::vector<std::string> GeneratePartitionNames(const std::string& base, int num_slots = 0);
+
+int ConfigureSerial(const std::string& port);
+
+int StartProgram(const std::string program, const std::vector<std::string> args, int* pipe);
+int WaitProgram(const pid_t pid, const int pipe, std::string* error_msg);
+
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp b/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
new file mode 100644
index 0000000..7c595f4
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
@@ -0,0 +1,193 @@
+#include "usb_transport_sniffer.h"
+#include <android-base/stringprintf.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <iomanip>
+#include <sstream>
+
+namespace fastboot {
+
+UsbTransportSniffer::UsbTransportSniffer(std::unique_ptr<UsbTransport> transport,
+                                         const int serial_fd)
+    : transport_(std::move(transport)), serial_fd_(serial_fd) {}
+
+UsbTransportSniffer::~UsbTransportSniffer() {
+    Close();
+}
+
+ssize_t UsbTransportSniffer::Read(void* data, size_t len) {
+    ProcessSerial();
+
+    ssize_t ret = transport_->Read(data, len);
+    if (ret < 0) {
+        const char* err = strerror(errno);
+        std::vector<char> buf(err, err + strlen(err));
+        Event e(READ_ERROR, std::move(buf));
+        transfers_.push_back(e);
+        return ret;
+    }
+
+    char* cdata = static_cast<char*>(data);
+    std::vector<char> buf(cdata, cdata + ret);
+    Event e(READ, std::move(buf));
+    transfers_.push_back(e);
+
+    ProcessSerial();
+    return ret;
+}
+
+ssize_t UsbTransportSniffer::Write(const void* data, size_t len) {
+    ProcessSerial();
+
+    size_t ret = transport_->Write(data, len);
+    if (ret != len) {
+        const char* err = strerror(errno);
+        std::vector<char> buf(err, err + strlen(err));
+        Event e(WRITE_ERROR, std::move(buf));
+        transfers_.push_back(e);
+        return ret;
+    }
+
+    const char* cdata = static_cast<const char*>(data);
+    std::vector<char> buf(cdata, cdata + len);
+    Event e(WRITE, std::move(buf));
+    transfers_.push_back(e);
+
+    ProcessSerial();
+    return ret;
+}
+
+int UsbTransportSniffer::Close() {
+    return transport_->Close();
+}
+
+int UsbTransportSniffer::Reset() {
+    ProcessSerial();
+    int ret = transport_->Reset();
+    std::vector<char> buf;
+    Event e(RESET, std::move(buf));
+    transfers_.push_back(e);
+    ProcessSerial();
+    return ret;
+}
+
+const std::vector<UsbTransportSniffer::Event> UsbTransportSniffer::Transfers() {
+    return transfers_;
+}
+
+/*
+ * When a test fails, we want a human readable log of everything going on up until
+ * the failure. This method will look through its log of captured events, and
+ * create a clean printable string of everything that happened.
+ */
+std::string UsbTransportSniffer::CreateTrace() {
+    std::string ret;
+
+    const auto no_print = [](char c) -> bool { return !isprint(c); };
+    // This lambda creates a humand readable representation of a byte buffer
+    // It first attempts to figure out whether it should be interpreted as an ASCII buffer,
+    // and be printed as a string, or just a raw byte-buffer
+    const auto msg = [&ret, no_print](const std::vector<char>& buf) {
+        ret += android::base::StringPrintf("(%lu bytes): ", buf.size());
+        std::vector<const char>::iterator iter = buf.end();
+        const unsigned max_chars = 50;
+        if (buf.size() > max_chars) {
+            iter = buf.begin() + max_chars;
+        }
+        ret += '"';
+        if (std::count_if(buf.begin(), iter, no_print) == 0) {  // print as ascii
+            ret.insert(ret.end(), buf.begin(), iter);
+        } else {  // print as hex
+            std::stringstream ss;
+            for (auto c = buf.begin(); c < iter; c++) {
+                ss << std::hex << std::setw(2) << std::setfill('0')
+                   << static_cast<uint16_t>(static_cast<uint8_t>(*c));
+                ss << ',';
+            }
+            ret += ss.str();
+        }
+        if (buf.size() > max_chars) {
+            ret += android::base::StringPrintf("...\"(+%lu bytes)\n", buf.size() - max_chars);
+        } else {
+            ret += "\"\n";
+        }
+    };
+
+    // Now we just scan through the log of everything that happened and create a
+    // printable string for each one
+    for (const auto& event : transfers_) {
+        const std::vector<char>& cbuf = event.buf;
+        const std::string tmp(cbuf.begin(), cbuf.end());
+        auto start = transfers_.front().start;
+        auto durr = event.start - start;
+        auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(durr).count();
+
+        switch (event.type) {
+            case READ:
+                ret += android::base::StringPrintf("[READ %lldms]", millis);
+                msg(cbuf);
+                break;
+
+            case WRITE:
+                ret += android::base::StringPrintf("[WRITE %lldms]", millis);
+                msg(cbuf);
+                break;
+
+            case RESET:
+                ret += android::base::StringPrintf("[RESET %lldms]\n", millis);
+                break;
+
+            case READ_ERROR:
+                ret += android::base::StringPrintf("[READ_ERROR %lldms] %s\n", millis, tmp.c_str());
+                break;
+
+            case WRITE_ERROR:
+                ret += android::base::StringPrintf("[WRITE_ERROR %lldms] %s\n", millis,
+                                                   tmp.c_str());
+                break;
+
+            case SERIAL:
+                ret += android::base::StringPrintf("[SERIAL %lldms] %s", millis, tmp.c_str());
+                if (ret.back() != '\n') ret += '\n';
+                break;
+        }
+    }
+    return ret;
+}
+
+// This is a quick call to flush any UART logs the device might have sent
+// to our internal event log. It will wait up to 10ms for data to appear
+void UsbTransportSniffer::ProcessSerial() {
+    if (serial_fd_ <= 0) return;
+
+    fd_set set;
+    struct timeval timeout;
+
+    FD_ZERO(&set);
+    FD_SET(serial_fd_, &set);
+    timeout.tv_sec = 0;
+    timeout.tv_usec = 10000;  // 10ms
+
+    int count = 0;
+    int n = 0;
+    std::vector<char> buf;
+    buf.resize(1000);
+    while (select(serial_fd_ + 1, &set, NULL, NULL, &timeout) > 0) {
+        n = read(serial_fd_, buf.data() + count, buf.size() - count);
+        if (n > 0) {
+            count += n;
+        } else {
+            break;
+        }
+    }
+
+    buf.resize(count);
+
+    if (count > 0) {
+        Event e(SERIAL, std::move(buf));
+        transfers_.push_back(e);
+    }
+}
+
+}  // namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h b/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
new file mode 100644
index 0000000..8119aea
--- /dev/null
+++ b/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <chrono>
+#include <cstdlib>
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include "usb.h"
+
+namespace fastboot {
+
+/* A special class for sniffing reads and writes
+ *
+ * A useful debugging tool is to see the raw fastboot transactions going between
+ * the host and device. This class wraps the UsbTransport class, and snoops and saves
+ * all the transactions going on. Additionally, if there is a console serial port
+ * from the device, this class can monitor it as well and capture the interleaving of
+ * transport transactions and UART log messages.
+ */
+class UsbTransportSniffer : public UsbTransport {
+  public:
+    enum EventType {
+        READ,
+        WRITE,
+        RESET,
+        SERIAL,  // Serial log message from device
+        READ_ERROR,
+        WRITE_ERROR,
+    };
+
+    struct Event {
+        Event(EventType t, const std::vector<char> cbuf) : type(t), buf(cbuf) {
+            start = std::chrono::high_resolution_clock::now();
+        };
+        EventType type;
+        std::chrono::high_resolution_clock::time_point start;
+        const std::vector<char> buf;
+    };
+
+    UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, const int serial_fd = 0);
+    ~UsbTransportSniffer() override;
+
+    virtual ssize_t Read(void* data, size_t len) override;
+    virtual ssize_t Write(const void* data, size_t len) override;
+    virtual int Close() override final;  // note usage in destructor
+    virtual int Reset() override;
+
+    const std::vector<Event> Transfers();
+    std::string CreateTrace();
+    void ProcessSerial();
+
+  private:
+    std::vector<Event> transfers_;
+    std::unique_ptr<UsbTransport> transport_;
+    const int serial_fd_;
+};
+
+}  // End namespace fastboot
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 9b779dd..6363aa5 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -95,7 +95,7 @@
   public:
     explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
         : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
-    ~LinuxUsbTransport() override = default;
+    ~LinuxUsbTransport() override;
 
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
@@ -387,6 +387,10 @@
     return usb;
 }
 
+LinuxUsbTransport::~LinuxUsbTransport() {
+    Close();
+}
+
 ssize_t LinuxUsbTransport::Write(const void* _data, size_t len)
 {
     unsigned char *data = (unsigned char*) _data;
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 4d48f6e..ed02c4a 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -70,7 +70,7 @@
     // A timeout of 0 is blocking
     OsxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
         : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
-    ~OsxUsbTransport() override = default;
+    ~OsxUsbTransport() override;
 
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
@@ -471,6 +471,10 @@
     return new OsxUsbTransport(std::move(handle), timeout_ms);
 }
 
+OsxUsbTransport::~OsxUsbTransport() {
+    Close();
+}
+
 int OsxUsbTransport::Close() {
     /* TODO: Something better here? */
     return 0;
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 8c60a71..b00edb3 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -69,7 +69,7 @@
 class WindowsUsbTransport : public UsbTransport {
   public:
     WindowsUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}
-    ~WindowsUsbTransport() override = default;
+    ~WindowsUsbTransport() override;
 
     ssize_t Read(void* data, size_t len) override;
     ssize_t Write(const void* data, size_t len) override;
@@ -250,6 +250,10 @@
     }
 }
 
+WindowsUsbTransport::~WindowsUsbTransport() {
+    Close();
+}
+
 int WindowsUsbTransport::Close() {
     DBG("usb_close\n");
 
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 8f6e52a..7d15047 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -70,8 +70,7 @@
     fprintf(stderr, "\n");
 }
 
-char* xstrdup(const char* s) {
-    char* result = strdup(s);
-    if (!result) die("out of memory");
-    return result;
+void Status(const std::string& message) {
+    static constexpr char kStatusFormat[] = "%-50s ";
+    fprintf(stderr, kStatusFormat, message.c_str());
 }
diff --git a/fastboot/util.h b/fastboot/util.h
index 9033f93..533d2c7 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -9,9 +9,10 @@
 
 /* util stuff */
 double now();
-char* xstrdup(const char*);
 void set_verbose();
 
+void Status(const std::string& message);
+
 // These printf-like functions are implemented in terms of vsnprintf, so they
 // use the same attribute for compile-time format string checking.
 void die(const char* fmt, ...) __attribute__((__noreturn__))
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 99f2df8..d29ccf4 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -79,7 +79,8 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
-using DeviceMapper = android::dm::DeviceMapper;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
 
 // record fs stat
 enum FsStatFlags {
@@ -101,12 +102,16 @@
 
 // TODO: switch to inotify()
 bool fs_mgr_wait_for_file(const std::string& filename,
-                          const std::chrono::milliseconds relative_timeout) {
+                          const std::chrono::milliseconds relative_timeout,
+                          FileWaitMode file_wait_mode) {
     auto start_time = std::chrono::steady_clock::now();
 
     while (true) {
-        if (!access(filename.c_str(), F_OK) || errno != ENOENT) {
-            return true;
+        int rv = access(filename.c_str(), F_OK);
+        if (file_wait_mode == FileWaitMode::Exists) {
+            if (!rv || errno != ENOENT) return true;
+        } else if (file_wait_mode == FileWaitMode::DoesNotExist) {
+            if (rv && errno == ENOENT) return true;
         }
 
         std::this_thread::sleep_for(50ms);
@@ -564,8 +569,7 @@
  *   -1 on failure with errno set to match the 1st mount failure.
  *   0 on success.
  */
-static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx)
-{
+static int mount_with_alternatives(fstab* fstab, int start_idx, int* end_idx, int* attempted_idx) {
     int i;
     int mount_errno = 0;
     int mounted = 0;
@@ -790,7 +794,8 @@
         argv.emplace_back(arg.c_str());
     }
     LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
-    int ret = android_fork_execvp(4, const_cast<char**>(argv.data()), nullptr, false, true);
+    int ret =
+            android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), nullptr, false, true);
     if (ret != 0) {
         LOG(ERROR) << "vdc returned error code: " << ret;
         return false;
@@ -799,6 +804,23 @@
     return true;
 }
 
+static bool call_vdc_ret(const std::vector<std::string>& args, int* ret) {
+    std::vector<char const*> argv;
+    argv.emplace_back("/system/bin/vdc");
+    for (auto& arg : args) {
+        argv.emplace_back(arg.c_str());
+    }
+    LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
+    int err = android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), ret, false, true);
+    if (err != 0) {
+        LOG(ERROR) << "vdc call failed with error code: " << err;
+        return false;
+    }
+    LOG(DEBUG) << "vdc finished successfully";
+    *ret = WEXITSTATUS(*ret);
+    return true;
+}
+
 bool fs_mgr_update_logical_partition(struct fstab_rec* rec) {
     // Logical partitions are specified with a named partition rather than a
     // block device, so if the block device is a path, then it has already
@@ -817,19 +839,37 @@
     return true;
 }
 
+bool fs_mgr_update_checkpoint_partition(struct fstab_rec* rec) {
+    if (fs_mgr_is_checkpoint(rec)) {
+        if (!strcmp(rec->fs_type, "f2fs")) {
+            std::string opts(rec->fs_options);
+
+            opts += ",checkpoint=disable";
+            free(rec->fs_options);
+            rec->fs_options = strdup(opts.c_str());
+        } else {
+            LERROR << rec->fs_type << " does not implement checkpoints.";
+        }
+    } else if (rec->fs_mgr_flags & MF_CHECKPOINT_BLK) {
+        LERROR << "Block based checkpoint not implemented.";
+        return false;
+    }
+    return true;
+}
+
 /* When multiple fstab records share the same mount_point, it will
  * try to mount each one in turn, and ignore any duplicates after a
  * first successful mount.
  * Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
  */
-int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
-{
+int fs_mgr_mount_all(fstab* fstab, int mount_mode) {
     int i = 0;
     int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
     int error_count = 0;
     int mret = -1;
     int mount_errno = 0;
     int attempted_idx = -1;
+    int need_checkpoint = -1;
     FsManagerAvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
@@ -852,7 +892,8 @@
         }
 
         /* Skip mounting the root partition, as it will already have been mounted */
-        if (!strcmp(fstab->recs[i].mount_point, "/")) {
+        if (!strcmp(fstab->recs[i].mount_point, "/") ||
+            !strcmp(fstab->recs[i].mount_point, "/system")) {
             if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) {
                 fs_mgr_set_blk_ro(fstab->recs[i].blk_device);
             }
@@ -875,6 +916,18 @@
             }
         }
 
+        if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
+            if (need_checkpoint == -1 &&
+                !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
+                LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+                need_checkpoint = 0;
+            }
+            if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up checkpoint partition, skipping!";
+                continue;
+            }
+        }
+
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
             !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
             LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
@@ -1038,7 +1091,7 @@
     }
 
 #if ALLOW_ADBD_DISABLE_VERITY == 1  // "userdebug" build
-    fs_mgr_overlayfs_mount_all();
+    fs_mgr_overlayfs_mount_all(fstab);
 #endif
 
     if (error_count) {
@@ -1074,9 +1127,8 @@
  * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
  * in turn, and stop on 1st success, or no more match.
  */
-int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
-                    char *tmp_mount_point)
-{
+static int fs_mgr_do_mount_helper(fstab* fstab, const char* n_name, char* n_blk_device,
+                                  char* tmp_mount_point, int need_checkpoint) {
     int i = 0;
     int mount_errors = 0;
     int first_mount_errno = 0;
@@ -1109,6 +1161,18 @@
             }
         }
 
+        if (fs_mgr_is_checkpoint(&fstab->recs[i])) {
+            if (need_checkpoint == -1 &&
+                !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &need_checkpoint)) {
+                LERROR << "Failed to find if checkpointing is needed. Assuming no.";
+                need_checkpoint = 0;
+            }
+            if (need_checkpoint == 1 && !fs_mgr_update_checkpoint_partition(&fstab->recs[i])) {
+                LERROR << "Could not set up checkpoint partition, skipping!";
+                continue;
+            }
+        }
+
         /* First check the filesystem if requested */
         if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
             LERROR << "Skipping mounting '" << n_blk_device << "'";
@@ -1178,6 +1242,15 @@
     return FS_MGR_DOMNT_FAILED;
 }
 
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1);
+}
+
+int fs_mgr_do_mount(fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
+                    bool needs_cp) {
+    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_cp);
+}
+
 /*
  * mount a tmpfs filesystem at the given point.
  * return 0 on success, non-zero on failure.
@@ -1200,8 +1273,7 @@
 /* This must be called after mount_all, because the mkswap command needs to be
  * available.
  */
-int fs_mgr_swapon_all(struct fstab *fstab)
-{
+int fs_mgr_swapon_all(fstab* fstab) {
     int i = 0;
     int flags = 0;
     int err = 0;
@@ -1291,7 +1363,7 @@
     return ret;
 }
 
-struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab) {
+struct fstab_rec const* fs_mgr_get_crypt_entry(fstab const* fstab) {
     int i;
 
     if (!fstab) {
@@ -1315,7 +1387,7 @@
  *
  * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long
  */
-void fs_mgr_get_crypt_info(struct fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
+void fs_mgr_get_crypt_info(fstab* fstab, char* key_loc, char* real_blk_device, size_t size) {
     struct fstab_rec const* rec = fs_mgr_get_crypt_entry(fstab);
     if (key_loc) {
         if (rec) {
@@ -1400,8 +1472,12 @@
             mount_point = basename(fstab->recs[i].mount_point);
         }
 
-        const char* status = nullptr;
+        if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
+            PERROR << "Could not find verity device for mount point: " << mount_point;
+            continue;
+        }
 
+        const char* status = nullptr;
         std::vector<DeviceMapper::TargetInfo> table;
         if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
             if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index a91e92e..804069a 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -81,7 +81,7 @@
 
 static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
                                    const LpMetadataPartition& partition, bool force_writable,
-                                   std::string* path) {
+                                   const std::chrono::milliseconds& timeout_ms, std::string* path) {
     DeviceMapper& dm = DeviceMapper::Instance();
 
     DmTable table;
@@ -98,6 +98,13 @@
     if (!dm.GetDmDevicePathByName(name, path)) {
         return false;
     }
+    if (timeout_ms > std::chrono::milliseconds::zero()) {
+        if (!fs_mgr_wait_for_file(*path, timeout_ms, FileWaitMode::Exists)) {
+            DestroyLogicalPartition(name, {});
+            LERROR << "Timed out waiting for device path: " << *path;
+            return false;
+        }
+    }
     LINFO << "Created logical partition " << name << " on device " << *path;
     return true;
 }
@@ -115,7 +122,7 @@
             continue;
         }
         std::string path;
-        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, &path)) {
+        if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, {}, &path)) {
             LERROR << "Could not create logical partition: " << GetPartitionName(partition);
             return false;
         }
@@ -125,7 +132,7 @@
 
 bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot,
                             const std::string& partition_name, bool force_writable,
-                            std::string* path) {
+                            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.";
@@ -134,18 +141,26 @@
     for (const auto& partition : metadata->partitions) {
         if (GetPartitionName(partition) == partition_name) {
             return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
-                                          path);
+                                          timeout_ms, path);
         }
     }
     LERROR << "Could not find any partition with name: " << partition_name;
     return false;
 }
 
-bool DestroyLogicalPartition(const std::string& name) {
+bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
     DeviceMapper& dm = DeviceMapper::Instance();
+    std::string path;
+    if (timeout_ms > std::chrono::milliseconds::zero()) {
+        dm.GetDmDevicePathByName(name, &path);
+    }
     if (!dm.DeleteDevice(name)) {
         return false;
     }
+    if (!path.empty() && !fs_mgr_wait_for_file(path, timeout_ms, FileWaitMode::DoesNotExist)) {
+        LERROR << "Timed out waiting for device path to unlink: " << path;
+        return false;
+    }
     LINFO << "Unmapped logical partition " << name;
     return true;
 }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index f87a3b1..250793a 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -80,37 +80,39 @@
 };
 
 static struct flag_list fs_mgr_flags[] = {
-    {"wait", MF_WAIT},
-    {"check", MF_CHECK},
-    {"encryptable=", MF_CRYPT},
-    {"forceencrypt=", MF_FORCECRYPT},
-    {"fileencryption=", MF_FILEENCRYPTION},
-    {"forcefdeorfbe=", MF_FORCEFDEORFBE},
-    {"keydirectory=", MF_KEYDIRECTORY},
-    {"nonremovable", MF_NONREMOVABLE},
-    {"voldmanaged=", MF_VOLDMANAGED},
-    {"length=", MF_LENGTH},
-    {"recoveryonly", MF_RECOVERYONLY},
-    {"swapprio=", MF_SWAPPRIO},
-    {"zramsize=", MF_ZRAMSIZE},
-    {"max_comp_streams=", MF_MAX_COMP_STREAMS},
-    {"verifyatboot", MF_VERIFYATBOOT},
-    {"verify", MF_VERIFY},
-    {"avb", MF_AVB},
-    {"noemulatedsd", MF_NOEMULATEDSD},
-    {"notrim", MF_NOTRIM},
-    {"formattable", MF_FORMATTABLE},
-    {"slotselect", MF_SLOTSELECT},
-    {"nofail", MF_NOFAIL},
-    {"latemount", MF_LATEMOUNT},
-    {"reservedsize=", MF_RESERVEDSIZE},
-    {"quota", MF_QUOTA},
-    {"eraseblk=", MF_ERASEBLKSIZE},
-    {"logicalblk=", MF_LOGICALBLKSIZE},
-    {"sysfs_path=", MF_SYSFS},
-    {"defaults", 0},
-    {"logical", MF_LOGICAL},
-    {0, 0},
+        {"wait", MF_WAIT},
+        {"check", MF_CHECK},
+        {"encryptable=", MF_CRYPT},
+        {"forceencrypt=", MF_FORCECRYPT},
+        {"fileencryption=", MF_FILEENCRYPTION},
+        {"forcefdeorfbe=", MF_FORCEFDEORFBE},
+        {"keydirectory=", MF_KEYDIRECTORY},
+        {"nonremovable", MF_NONREMOVABLE},
+        {"voldmanaged=", MF_VOLDMANAGED},
+        {"length=", MF_LENGTH},
+        {"recoveryonly", MF_RECOVERYONLY},
+        {"swapprio=", MF_SWAPPRIO},
+        {"zramsize=", MF_ZRAMSIZE},
+        {"max_comp_streams=", MF_MAX_COMP_STREAMS},
+        {"verifyatboot", MF_VERIFYATBOOT},
+        {"verify", MF_VERIFY},
+        {"avb", MF_AVB},
+        {"noemulatedsd", MF_NOEMULATEDSD},
+        {"notrim", MF_NOTRIM},
+        {"formattable", MF_FORMATTABLE},
+        {"slotselect", MF_SLOTSELECT},
+        {"nofail", MF_NOFAIL},
+        {"latemount", MF_LATEMOUNT},
+        {"reservedsize=", MF_RESERVEDSIZE},
+        {"quota", MF_QUOTA},
+        {"eraseblk=", MF_ERASEBLKSIZE},
+        {"logicalblk=", MF_LOGICALBLKSIZE},
+        {"sysfs_path=", MF_SYSFS},
+        {"defaults", 0},
+        {"logical", MF_LOGICAL},
+        {"checkpoint=block", MF_CHECKPOINT_BLK},
+        {"checkpoint=fs", MF_CHECKPOINT_FS},
+        {0, 0},
 };
 
 #define EM_AES_256_XTS  1
@@ -1004,3 +1006,15 @@
 int fs_mgr_is_logical(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_LOGICAL;
 }
+
+int fs_mgr_is_checkpoint(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & (MF_CHECKPOINT_FS | MF_CHECKPOINT_BLK);
+}
+
+int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_CHECKPOINT_FS;
+}
+
+int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab) {
+    return fstab->fs_mgr_flags & MF_CHECKPOINT_BLK;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 78151d5..c0bef2c 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -16,6 +16,7 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <linux/fs.h>
 #include <selinux/selinux.h>
 #include <stdio.h>
@@ -23,7 +24,9 @@
 #include <sys/mount.h>
 #include <sys/param.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/types.h>
+#include <sys/vfs.h>
 #include <unistd.h>
 
 #include <map>
@@ -35,6 +38,8 @@
 #include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 
@@ -44,7 +49,7 @@
 
 #if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
 
-bool fs_mgr_overlayfs_mount_all() {
+bool fs_mgr_overlayfs_mount_all(const fstab*) {
     return false;
 }
 
@@ -96,38 +101,56 @@
     return "";
 }
 
-bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
-    // readonly filesystem, can not be mount -o remount,rw
-    return "squashfs"s == fsrec->fs_type;
+// At less than 1% free space return value of false,
+// means we will try to wrap with overlayfs.
+bool fs_mgr_filesystem_has_space(const char* mount_point) {
+    // If we have access issues to find out space remaining, return true
+    // to prevent us trying to override with overlayfs.
+    struct statvfs vst;
+    if (statvfs(mount_point, &vst)) return true;
+
+    static constexpr int kPercentThreshold = 1;  // 1%
+
+    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100));
 }
 
-constexpr char upper_name[] = "upper";
-constexpr char work_name[] = "work";
+bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
+    // readonly filesystem, can not be mount -o remount,rw
+    // if squashfs or if free space is (near) zero making such a remount
+    // virtually useless, or if there are shared blocks that prevent remount,rw
+    return ("squashfs"s == fsrec->fs_type) ||
+           fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device) ||
+           !fs_mgr_filesystem_has_space(fsrec->mount_point);
+}
+
+const auto kUpperName = "upper"s;
+const auto kWorkName = "work"s;
+const auto kOverlayTopDir = "/overlay"s;
 
 std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
     if (!fs_mgr_is_dir(mount_point)) return "";
-    auto dir = kOverlayMountPoint + "/overlay/" + android::base::Basename(mount_point) + "/";
-    auto upper = dir + upper_name;
+    auto dir =
+            kOverlayMountPoint + kOverlayTopDir + "/" + android::base::Basename(mount_point) + "/";
+    auto upper = dir + kUpperName;
     if (!fs_mgr_is_dir(upper)) return "";
-    auto work = dir + work_name;
+    auto work = dir + kWorkName;
     if (!fs_mgr_is_dir(work)) return "";
     if (!fs_mgr_dir_is_writable(work)) return "";
     return dir;
 }
 
-constexpr char lowerdir_option[] = "lowerdir=";
-constexpr char upperdir_option[] = "upperdir=";
+const auto kLowerdirOption = "lowerdir="s;
+const auto kUpperdirOption = "upperdir="s;
 
 // default options for mount_point, returns empty string for none available.
-std::string fs_mgr_get_overlayfs_options(const char* mount_point) {
-    auto fsrec_mount_point = std::string(mount_point);
-    auto candidate = fs_mgr_get_overlayfs_candidate(fsrec_mount_point);
+std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
+    auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
     if (candidate.empty()) return "";
 
-    auto context = fs_mgr_get_context(fsrec_mount_point);
+    auto context = fs_mgr_get_context(mount_point);
     if (!context.empty()) context = ",rootcontext="s + context;
-    return "override_creds=off,"s + lowerdir_option + fsrec_mount_point + "," + upperdir_option +
-           candidate + upper_name + ",workdir=" + candidate + work_name + context;
+    return "override_creds=off,"s + kLowerdirOption + mount_point + "," + kUpperdirOption +
+           candidate + kUpperName + ",workdir=" + candidate + kWorkName + context;
 }
 
 bool fs_mgr_system_root_image(const fstab* fstab) {
@@ -145,10 +168,11 @@
     return true;
 }
 
-std::string fs_mgr_get_overlayfs_options(const fstab* fstab, const char* mount_point) {
-    if (fs_mgr_system_root_image(fstab) && ("/"s == mount_point)) mount_point = "/system";
-
-    return fs_mgr_get_overlayfs_options(mount_point);
+const char* fs_mgr_mount_point(const fstab* fstab, const char* mount_point) {
+    if (!mount_point) return mount_point;
+    if ("/"s != mount_point) return mount_point;
+    if (!fs_mgr_system_root_image(fstab)) return mount_point;
+    return "/system";
 }
 
 // return true if system supports overlayfs
@@ -170,11 +194,37 @@
     return overlayfs_in_kernel;
 }
 
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab("/proc/mounts"),
+                                                               fs_mgr_free_fstab);
+    if (!fstab) return false;
+    const auto lowerdir = kLowerdirOption + mount_point;
+    for (auto i = 0; i < fstab->num_entries; ++i) {
+        const auto fsrec = &fstab->recs[i];
+        const auto fs_type = fsrec->fs_type;
+        if (!fs_type) continue;
+        if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
+        auto fsrec_mount_point = fsrec->mount_point;
+        if (!fsrec_mount_point) continue;
+        if (mount_point != fsrec_mount_point) continue;
+        const auto fs_options = fsrec->fs_options;
+        if (!fs_options) continue;
+        const auto options = android::base::Split(fs_options, ",");
+        for (const auto& opt : options) {
+            if (opt == lowerdir) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 bool fs_mgr_wants_overlayfs(const fstab_rec* fsrec) {
     if (!fsrec) return false;
 
     auto fsrec_mount_point = fsrec->mount_point;
-    if (!fsrec_mount_point) return false;
+    if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
+    if (!fsrec->blk_device) return false;
 
     if (!fsrec->fs_type) return false;
 
@@ -242,10 +292,17 @@
     return ret;
 }
 
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
+
 bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
                                 bool* change) {
     auto ret = true;
-    auto fsrec_mount_point = overlay + android::base::Basename(mount_point) + "/";
+    auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
+
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        ret = false;
+        PERROR << "overlayfs setfscreatecon " << kOverlayfsFileContext;
+    }
     auto save_errno = errno;
     if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
         if (change) *change = true;
@@ -257,21 +314,22 @@
     }
 
     save_errno = errno;
-    if (!mkdir((fsrec_mount_point + work_name).c_str(), 0755)) {
+    if (!mkdir((fsrec_mount_point + kWorkName).c_str(), 0755)) {
         if (change) *change = true;
     } else if (errno != EEXIST) {
         ret = false;
-        PERROR << "overlayfs mkdir " << fsrec_mount_point << work_name;
+        PERROR << "overlayfs mkdir " << fsrec_mount_point << kWorkName;
     } else {
         errno = save_errno;
     }
+    setfscreatecon(nullptr);
 
     auto new_context = fs_mgr_get_context(mount_point);
     if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
         ret = false;
         PERROR << "overlayfs setfscreatecon " << new_context;
     }
-    auto upper = fsrec_mount_point + upper_name;
+    auto upper = fsrec_mount_point + kUpperName;
     save_errno = errno;
     if (!mkdir(upper.c_str(), 0755)) {
         if (change) *change = true;
@@ -286,25 +344,22 @@
     return ret;
 }
 
-bool fs_mgr_overlayfs_mount(const fstab* fstab, const fstab_rec* fsrec) {
-    if (!fs_mgr_wants_overlayfs(fsrec)) return false;
-    auto fsrec_mount_point = fsrec->mount_point;
-    if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
-    auto options = fs_mgr_get_overlayfs_options(fstab, fsrec_mount_point);
+bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
+    auto options = fs_mgr_get_overlayfs_options(mount_point);
     if (options.empty()) return false;
 
     // hijack __mount() report format to help triage
-    auto report = "__mount(source=overlay,target="s + fsrec_mount_point + ",type=overlay";
+    auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
     const auto opt_list = android::base::Split(options, ",");
     for (const auto opt : opt_list) {
-        if (android::base::StartsWith(opt, upperdir_option)) {
+        if (android::base::StartsWith(opt, kUpperdirOption)) {
             report = report + "," + opt;
             break;
         }
     }
     report = report + ")=";
 
-    auto ret = mount("overlay", fsrec_mount_point, "overlay", MS_RDONLY | MS_RELATIME,
+    auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
                      options.c_str());
     if (ret) {
         PERROR << report << ret;
@@ -315,50 +370,46 @@
     }
 }
 
-bool fs_mgr_overlayfs_already_mounted(const char* mount_point) {
-    if (!mount_point) return false;
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
-            fs_mgr_read_fstab("/proc/mounts"), fs_mgr_free_fstab);
-    if (!fstab) return false;
-    const auto lowerdir = std::string(lowerdir_option) + mount_point;
-    for (auto i = 0; i < fstab->num_entries; ++i) {
+std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
+                                               const char* mount_point = nullptr) {
+    std::vector<std::string> mounts;
+    if (!fstab) return mounts;
+
+    for (auto i = 0; i < fstab->num_entries; i++) {
         const auto fsrec = &fstab->recs[i];
-        const auto fs_type = fsrec->fs_type;
-        if (!fs_type) continue;
-        if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
-        auto fsrec_mount_point = fsrec->mount_point;
-        if (!fsrec_mount_point) continue;
-        if (strcmp(fsrec_mount_point, mount_point)) continue;
-        const auto fs_options = fsrec->fs_options;
-        if (!fs_options) continue;
-        const auto options = android::base::Split(fs_options, ",");
-        for (const auto opt : options) {
-            if (opt == lowerdir) {
-                return true;
+        if (!fs_mgr_wants_overlayfs(fsrec)) continue;
+        std::string new_mount_point(fs_mgr_mount_point(fstab, fsrec->mount_point));
+        if (mount_point && (new_mount_point != mount_point)) continue;
+        auto duplicate_or_more_specific = false;
+        for (auto it = mounts.begin(); it != mounts.end();) {
+            if ((*it == new_mount_point) ||
+                (android::base::StartsWith(new_mount_point, *it + "/"))) {
+                duplicate_or_more_specific = true;
+                break;
+            }
+            if (android::base::StartsWith(*it, new_mount_point + "/")) {
+                it = mounts.erase(it);
+            } else {
+                ++it;
             }
         }
+        if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
     }
-    return false;
+    return mounts;
 }
 
 }  // namespace
 
-bool fs_mgr_overlayfs_mount_all() {
+bool fs_mgr_overlayfs_mount_all(const fstab* fstab) {
     auto ret = false;
 
     if (!fs_mgr_wants_overlayfs()) return ret;
 
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                                      fs_mgr_free_fstab);
     if (!fstab) return ret;
 
-    for (auto i = 0; i < fstab->num_entries; i++) {
-        const auto fsrec = &fstab->recs[i];
-        auto fsrec_mount_point = fsrec->mount_point;
-        if (!fsrec_mount_point) continue;
-        if (fs_mgr_overlayfs_already_mounted(fsrec_mount_point)) continue;
-
-        if (fs_mgr_overlayfs_mount(fstab.get(), fsrec)) ret = true;
+    for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
+        if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+        if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
     }
     return ret;
 }
@@ -379,26 +430,16 @@
         return ret;
     }
 
-    std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
-                                                                      fs_mgr_free_fstab);
-    std::vector<std::string> mounts;
-    if (fstab) {
-        if (!fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
-        for (auto i = 0; i < fstab->num_entries; i++) {
-            const auto fsrec = &fstab->recs[i];
-            auto fsrec_mount_point = fsrec->mount_point;
-            if (!fsrec_mount_point) continue;
-            if (mount_point && strcmp(fsrec_mount_point, mount_point)) continue;
-            if (!fs_mgr_wants_overlayfs(fsrec)) continue;
-            mounts.emplace_back(fsrec_mount_point);
-        }
-        if (mounts.empty()) return ret;
-    }
+    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
+                                                               fs_mgr_free_fstab);
+    if (fstab && !fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
+    auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(fstab.get(), mount_point));
+    if (fstab && mounts.empty()) return ret;
 
-    if (mount_point && ("/"s == mount_point) && fs_mgr_system_root_image(fstab.get())) {
-        mount_point = "/system";
+    if (setfscreatecon(kOverlayfsFileContext)) {
+        PERROR << "overlayfs setfscreatecon " << kOverlayfsFileContext;
     }
-    auto overlay = kOverlayMountPoint + "/overlay/";
+    auto overlay = kOverlayMountPoint + kOverlayTopDir;
     auto save_errno = errno;
     if (!mkdir(overlay.c_str(), 0755)) {
         if (change) *change = true;
@@ -407,6 +448,7 @@
     } else {
         errno = save_errno;
     }
+    setfscreatecon(nullptr);
     if (!fstab && mount_point && fs_mgr_overlayfs_setup_one(overlay, mount_point, change)) {
         ret = true;
     }
@@ -420,14 +462,13 @@
 // If something is altered, set *change.
 bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
     if (change) *change = false;
-    if (mount_point && ("/"s == mount_point)) {
-        std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
-                fs_mgr_read_fstab_default(), fs_mgr_free_fstab);
-        if (fs_mgr_system_root_image(fstab.get())) mount_point = "/system";
-    }
+    mount_point = fs_mgr_mount_point(std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)>(
+                                             fs_mgr_read_fstab_default(), fs_mgr_free_fstab)
+                                             .get(),
+                                     mount_point);
     auto ret = true;
-    const auto overlay = kOverlayMountPoint + "/overlay";
-    const auto oldpath = overlay + (mount_point ?: "");
+    const auto overlay = kOverlayMountPoint + kOverlayTopDir;
+    const auto oldpath = overlay + (mount_point ? "/"s + mount_point : ""s);
     const auto newpath = oldpath + ".teardown";
     ret &= fs_mgr_rm_all(newpath);
     auto save_errno = errno;
@@ -477,3 +518,25 @@
 }
 
 #endif  // ALLOW_ADBD_DISABLE_VERITY != 0
+
+bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
+    struct statfs fs;
+    if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
+        (fs.f_type != EXT4_SUPER_MAGIC)) {
+        return false;
+    }
+
+    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) return false;
+
+    struct ext4_super_block sb;
+    if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
+        (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
+        return false;
+    }
+
+    struct fs_info info;
+    if (ext4_parse_sb(&sb, &info) < 0) return false;
+
+    return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index a347faf..506e81d 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -113,15 +113,21 @@
 #define MF_KEYDIRECTORY    0X4000000
 #define MF_SYSFS           0X8000000
 #define MF_LOGICAL        0x10000000
+#define MF_CHECKPOINT_BLK 0x20000000
+#define MF_CHECKPOINT_FS  0x40000000
 // clang-format on
 
 #define DM_BUF_SIZE 4096
 
 using namespace std::chrono_literals;
 
-int fs_mgr_set_blk_ro(const char *blockdev);
+enum class FileWaitMode { Exists, DoesNotExist };
+
 bool fs_mgr_wait_for_file(const std::string& filename,
-                          const std::chrono::milliseconds relative_timeout);
+                          const std::chrono::milliseconds relative_timeout,
+                          FileWaitMode wait_mode = FileWaitMode::Exists);
+
+int fs_mgr_set_blk_ro(const char* blockdev);
 bool fs_mgr_update_for_slotselect(struct fstab *fstab);
 bool fs_mgr_is_device_unlocked();
 const std::string& get_android_dt_dir();
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 1049fb6..cee069b 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -70,6 +70,8 @@
 
 int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
+int fs_mgr_do_mount(struct fstab* fstab, const char* n_name, char* n_blk_device,
+                    char* tmp_mount_point, bool need_cp);
 int fs_mgr_do_mount_one(struct fstab_rec *rec);
 int fs_mgr_do_tmpfs_mount(const char *n_name);
 struct fstab_rec const* fs_mgr_get_crypt_entry(struct fstab const* fstab);
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index f15c450..08f4554 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -27,6 +27,7 @@
 
 #include <stdint.h>
 
+#include <chrono>
 #include <memory>
 #include <string>
 #include <vector>
@@ -43,12 +44,16 @@
 // 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,
-                            std::string* path);
+                            const std::chrono::milliseconds& timeout_ms, std::string* path);
 
-// Destroy the block device for a logical partition, by name.
-bool DestroyLogicalPartition(const std::string& name);
+// Destroy the block device for a logical partition, by name. If |timeout_ms|
+// is non-zero, then this will block until the device path has been unlinked.
+bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms);
 
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 1d2ff03..251dd9b 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -18,7 +18,10 @@
 
 #include <fstab/fstab.h>
 
-bool fs_mgr_overlayfs_mount_all();
+#include <string>
+
+bool fs_mgr_overlayfs_mount_all(const fstab* fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
+bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index b1ee328..bb40511 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -86,6 +86,9 @@
 int fs_mgr_is_latemount(const struct fstab_rec* fstab);
 int fs_mgr_is_quota(const struct fstab_rec* fstab);
 int fs_mgr_is_logical(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint_fs(const struct fstab_rec* fstab);
+int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab);
 int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
 
 std::string fs_mgr_get_slot_suffix();
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index ad3b6f4..c4b57c7 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,11 +20,19 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 
+#include <android-base/logging.h>
 #include <android-base/macros.h>
 
 namespace android {
 namespace dm {
 
+DeviceMapper::DeviceMapper() : fd_(-1) {
+    fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
+    if (fd_ < 0) {
+        PLOG(ERROR) << "Failed to open device-mapper";
+    }
+}
+
 DeviceMapper& DeviceMapper::Instance() {
     static DeviceMapper instance;
     return instance;
@@ -90,9 +98,16 @@
     return nullptr;
 }
 
-DmDeviceState DeviceMapper::state(const std::string& /* name */) const {
-    // TODO(b/110035986): Return the state, as read from the kernel instead
-    return DmDeviceState::INVALID;
+DmDeviceState DeviceMapper::GetState(const std::string& name) const {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        return DmDeviceState::INVALID;
+    }
+    if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {
+        return DmDeviceState::ACTIVE;
+    }
+    return DmDeviceState::SUSPENDED;
 }
 
 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
@@ -263,7 +278,7 @@
     struct dm_ioctl io;
     InitIo(&io, name);
     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
-        PLOG(ERROR) << "DM_DEV_STATUS failed for " << name;
+        PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
         return false;
     }
 
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index e2bc729..91f8bb4 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -29,8 +29,6 @@
 #include <utility>
 #include <vector>
 
-#include <android-base/logging.h>
-
 #include "dm_table.h"
 
 // The minimum expected device mapper major.minor version
@@ -81,7 +79,7 @@
     // Returns the current state of the underlying device mapper device
     // with given name.
     // One of INVALID, SUSPENDED or ACTIVE.
-    DmDeviceState state(const std::string& name) const;
+    DmDeviceState GetState(const std::string& name) const;
 
     // Creates a device, loads the given table, and activates it. If the device
     // is not able to be activated, it is destroyed, and false is returned.
@@ -144,12 +142,7 @@
 
     void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
 
-    DeviceMapper() : fd_(-1) {
-        fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
-        if (fd_ < 0) {
-            PLOG(ERROR) << "Failed to open device-mapper";
-        }
-    }
+    DeviceMapper();
 
     // Creates a device mapper device with given name.
     // Return 'true' on success and 'false' on failure to
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 31863c8..aab89e5 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -23,8 +23,6 @@
 #include <string>
 #include <vector>
 
-#include <android-base/logging.h>
-
 namespace android {
 namespace dm {
 
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 2015e4d..352647b 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -386,6 +386,7 @@
     }
 
     const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE;
+    CHECK_NE(sectors_per_block, 0);
     CHECK(sectors_needed % sectors_per_block == 0);
 
     // Find gaps that we can use for new extents. Note we store new extents in a
@@ -481,6 +482,14 @@
     return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
 }
 
+uint64_t MetadataBuilder::UsedSpace() const {
+    uint64_t size = 0;
+    for (const auto& partition : partitions_) {
+        size += partition->size();
+    }
+    return size;
+}
+
 uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
     // Note: when reading alignment info from the Kernel, we don't assume it
     // is aligned to the sector size, so we round up to the nearest sector.
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index da9c8f3..0c7e43d 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -202,14 +202,28 @@
 }
 
 TEST(liblp, UseAllDiskSpace) {
-    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
-    EXPECT_EQ(builder->AllocatableSpace(), 1036288);
+    static constexpr uint64_t total = 1024 * 1024;
+    static constexpr uint64_t metadata = 1024;
+    static constexpr uint64_t slots = 2;
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(total, metadata, slots);
+    // We reserve a geometry block (4KB) plus space for each copy of the
+    // maximum size of a metadata blob. Then, we double that space since
+    // we store a backup copy of everything.
+    static constexpr uint64_t geometry = 4 * 1024;
+    static constexpr uint64_t allocatable = total - (metadata * slots + geometry) * 2;
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), 0);
 
     Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY);
     ASSERT_NE(system, nullptr);
-    EXPECT_EQ(builder->ResizePartition(system, 1036288), true);
-    EXPECT_EQ(system->size(), 1036288);
-    EXPECT_EQ(builder->ResizePartition(system, 1036289), false);
+    EXPECT_EQ(builder->ResizePartition(system, allocatable), true);
+    EXPECT_EQ(system->size(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), allocatable);
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
+    EXPECT_EQ(builder->ResizePartition(system, allocatable + 1), false);
+    EXPECT_EQ(system->size(), allocatable);
+    EXPECT_EQ(builder->UsedSpace(), allocatable);
+    EXPECT_EQ(builder->AllocatableSpace(), allocatable);
 }
 
 TEST(liblp, BuildComplex) {
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index a361a5d..742b1d0 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -38,12 +38,24 @@
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE;
         return nullptr;
     }
-    std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
-    if (!metadata) {
+    return ParseMetadata(geometry, fd);
+}
+
+std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes) {
+    if (bytes < LP_METADATA_GEOMETRY_SIZE) {
+        LERROR << __PRETTY_FUNCTION__ << ": " << bytes << " is smaller than geometry header";
         return nullptr;
     }
-    metadata->geometry = geometry;
-    return metadata;
+
+    LpMetadataGeometry geometry;
+    if (!ParseGeometry(data, &geometry)) {
+        return nullptr;
+    }
+
+    const uint8_t* metadata_buffer =
+            reinterpret_cast<const uint8_t*>(data) + LP_METADATA_GEOMETRY_SIZE;
+    size_t metadata_buffer_size = bytes - LP_METADATA_GEOMETRY_SIZE;
+    return ParseMetadata(geometry, metadata_buffer, metadata_buffer_size);
 }
 
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index a35cf8e..2780825 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -184,6 +184,7 @@
 
     // Amount of space that can be allocated to logical partitions.
     uint64_t AllocatableSpace() const;
+    uint64_t UsedSpace() const;
 
     // Merge new block device information into previous values. Alignment values
     // are only overwritten if the new values are non-zero.
@@ -192,6 +193,10 @@
 
   private:
     MetadataBuilder();
+    MetadataBuilder(const MetadataBuilder&) = delete;
+    MetadataBuilder(MetadataBuilder&&) = delete;
+    MetadataBuilder& operator=(const MetadataBuilder&) = delete;
+    MetadataBuilder& operator=(MetadataBuilder&&) = delete;
     bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
     bool Init(const LpMetadata& metadata);
     bool GrowPartition(Partition* partition, uint64_t aligned_size);
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 627aa8c..6da24f6 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -42,8 +42,7 @@
 // existing geometry, and should not be used for normal partition table
 // updates. False can be returned if the geometry is incompatible with the
 // block device or an I/O error occurs.
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
-                         uint32_t slot_number);
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata);
 
 // Update the partition table for a given metadata slot number. False is
 // returned if an error occurs, which can include:
@@ -64,6 +63,7 @@
                        const std::map<std::string, std::string>& images);
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
+std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
 
 // Helper to extract safe C++ strings from partition info.
 std::string GetPartitionName(const LpMetadataPartition& partition);
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 638f4b3..eda68fd 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -103,7 +103,7 @@
     if (!exported) {
         return {};
     }
-    if (!FlashPartitionTable(fd, *exported.get(), 0)) {
+    if (!FlashPartitionTable(fd, *exported.get())) {
         return {};
     }
     return fd;
@@ -117,6 +117,9 @@
     uint64_t size;
     ASSERT_TRUE(GetDescriptorSize(fd, &size));
     ASSERT_EQ(size, kDiskSize);
+
+    // Verify that we can't read unwritten metadata.
+    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
 }
 
 // Flashing metadata should not work if the metadata was created for a larger
@@ -132,7 +135,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get(), 0));
+    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get()));
 }
 
 // Test the basics of flashing a partition and reading it back.
@@ -147,7 +150,7 @@
     // Export and flash.
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
 
     // Read back. Note that some fields are only filled in during
     // serialization, so exported and imported will not be identical. For
@@ -191,9 +194,6 @@
     ASSERT_EQ(imported->partitions.size(), 1);
     EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
 
-    // Verify that we can't read unwritten metadata.
-    ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
-
     // Change the name before writing to the next slot.
     strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
     ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
@@ -354,8 +354,7 @@
     ASSERT_GE(fd, 0);
 
     // Check that we are able to write our table.
-    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
-    ASSERT_TRUE(UpdatePartitionTable(fd, *exported.get(), 1));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get()));
 
     // Check that adding one more partition overflows the metadata allotment.
     partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
@@ -395,6 +394,27 @@
     ASSERT_NE(imported, nullptr);
 }
 
+// Test that we can read images from buffers.
+TEST(liblp, ImageFilesInMemory) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+    unique_ptr<LpMetadata> exported = builder->Export();
+
+    unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+    ASSERT_GE(fd, 0);
+    ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));
+
+    int64_t offset = SeekFile64(fd, 0, SEEK_CUR);
+    ASSERT_GE(offset, 0);
+    ASSERT_EQ(SeekFile64(fd, 0, SEEK_SET), 0);
+
+    size_t bytes = static_cast<size_t>(offset);
+    std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bytes);
+    ASSERT_TRUE(android::base::ReadFully(fd, buffer.get(), bytes));
+    ASSERT_NE(ReadFromImageBlob(buffer.get(), bytes), nullptr);
+}
+
 class BadWriter {
   public:
     // When requested, write garbage instead of the requested bytes, then
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 117da59..190c650 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -30,9 +30,45 @@
 namespace android {
 namespace fs_mgr {
 
-// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
-// LP_METADATA_GEOMETRY_SIZE bytes in size.
-static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+// Helper class for reading descriptors and memory buffers in the same manner.
+class Reader {
+  public:
+    virtual ~Reader(){};
+    virtual bool ReadFully(void* buffer, size_t length) = 0;
+};
+
+class FileReader final : public Reader {
+  public:
+    explicit FileReader(int fd) : fd_(fd) {}
+    bool ReadFully(void* buffer, size_t length) override {
+        return android::base::ReadFully(fd_, buffer, length);
+    }
+
+  private:
+    int fd_;
+};
+
+class MemoryReader final : public Reader {
+  public:
+    MemoryReader(const void* buffer, size_t size)
+        : buffer_(reinterpret_cast<const uint8_t*>(buffer)), size_(size), pos_(0) {}
+    bool ReadFully(void* out, size_t length) override {
+        if (size_ - pos_ < length) {
+            errno = EINVAL;
+            return false;
+        }
+        memcpy(out, buffer_ + pos_, length);
+        pos_ += length;
+        return true;
+    }
+
+  private:
+    const uint8_t* buffer_;
+    size_t size_;
+    size_t pos_;
+};
+
+bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
     static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
     memcpy(geometry, buffer, sizeof(*geometry));
 
@@ -171,16 +207,18 @@
 
 // Parse and validate all metadata at the current position in the given file
 // descriptor.
-std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
+static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry,
+                                                 Reader* reader) {
     // First read and validate the header.
     std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
-    if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) {
+    if (!reader->ReadFully(&metadata->header, sizeof(metadata->header))) {
         PERROR << __PRETTY_FUNCTION__ << "read " << sizeof(metadata->header) << "bytes failed";
         return nullptr;
     }
     if (!ValidateMetadataHeader(metadata->header)) {
         return nullptr;
     }
+    metadata->geometry = geometry;
 
     LpMetadataHeader& header = metadata->header;
 
@@ -191,7 +229,7 @@
         LERROR << "Out of memory reading logical partition tables.";
         return nullptr;
     }
-    if (!android::base::ReadFully(fd, buffer.get(), header.tables_size)) {
+    if (!reader->ReadFully(buffer.get(), header.tables_size)) {
         PERROR << __PRETTY_FUNCTION__ << "read " << header.tables_size << "bytes failed";
         return nullptr;
     }
@@ -231,10 +269,20 @@
 
         metadata->extents.push_back(extent);
     }
-
     return metadata;
 }
 
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
+                                          size_t size) {
+    MemoryReader reader(buffer, size);
+    return ParseMetadata(geometry, &reader);
+}
+
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd) {
+    FileReader reader(fd);
+    return ParseMetadata(geometry, &reader);
+}
+
 std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
                                                 uint32_t slot_number) {
     int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
@@ -242,7 +290,7 @@
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
         return nullptr;
     }
-    return ParseMetadata(fd);
+    return ParseMetadata(geometry, fd);
 }
 
 std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
@@ -252,7 +300,7 @@
         PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
         return nullptr;
     }
-    return ParseMetadata(fd);
+    return ParseMetadata(geometry, fd);
 }
 
 std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
@@ -266,15 +314,12 @@
         return nullptr;
     }
 
-    // Read the priamry copy, and if that fails, try the backup.
+    // Read the primary copy, and if that fails, try the backup.
     std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number);
-    if (!metadata) {
-        metadata = ReadBackupMetadata(fd, geometry, slot_number);
-    }
     if (metadata) {
-        metadata->geometry = geometry;
+        return metadata;
     }
-    return metadata;
+    return ReadBackupMetadata(fd, geometry, slot_number);
 }
 
 std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index 843b2f2..9f6ca6e 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -26,11 +26,16 @@
 namespace android {
 namespace fs_mgr {
 
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
 
 // Helper functions for manually reading geometry and metadata.
+std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
+                                          size_t size);
 bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
-std::unique_ptr<LpMetadata> ParseMetadata(int fd);
 
 // These functions assume a valid geometry and slot number.
 std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index fc9d83f..9dd2745 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -94,7 +94,8 @@
     }
     // Make sure we're writing within the space reserved.
     if (blob->size() > geometry.metadata_max_size) {
-        LERROR << "Logical partition metadata is too large.";
+        LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
+               << geometry.metadata_max_size;
         return false;
     }
 
@@ -196,7 +197,7 @@
     return android::base::WriteFully(fd, blob.data(), blob.size());
 }
 
-bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) {
+bool FlashPartitionTable(int fd, const LpMetadata& metadata) {
     // Before writing geometry and/or logical partition tables, perform some
     // basic checks that the geometry and tables are coherent, and will fit
     // on the given block device.
@@ -224,8 +225,11 @@
         return false;
     }
 
-    // Write metadata to the correct slot, now that geometry is in place.
-    return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter);
+    bool ok = true;
+    for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
+        ok &= WriteMetadata(fd, metadata.geometry, i, metadata_blob, DefaultWriter);
+    }
+    return ok;
 }
 
 static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
@@ -297,14 +301,13 @@
     return WriteMetadata(fd, geometry, slot_number, blob, writer);
 }
 
-bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
-                         uint32_t slot_number) {
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {
     android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
         return false;
     }
-    if (!FlashPartitionTable(fd, metadata, slot_number)) {
+    if (!FlashPartitionTable(fd, metadata)) {
         return false;
     }
     LWARN << "Flashed new logical partition geometry to " << block_device;
diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h
index adbbebf..ab18d45 100644
--- a/fs_mgr/liblp/writer.h
+++ b/fs_mgr/liblp/writer.h
@@ -30,7 +30,7 @@
 
 // These variants are for testing only. The path-based functions should be used
 // for actual operation, so that open() is called with the correct flags.
-bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+bool FlashPartitionTable(int fd, const LpMetadata& metadata);
 bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
 
 bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number,
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 45a81af..879ba21 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/unique_fd.h>
 #include <libdm/dm.h>
diff --git a/healthd/Android.mk b/healthd/Android.mk
index f7214c6..9096f79 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -43,7 +43,7 @@
     AnimationParser.cpp
 
 LOCAL_MODULE := libhealthd_charger
-LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
     $(LOCAL_PATH) \
     $(LOCAL_PATH)/include
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 30f2cf3..706dc80 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -173,7 +173,7 @@
         cur_level = 100;
     }
 
-    if (cur_level <= 0) return;
+    if (cur_level < 0) return;
 
     const animation::text_field& field = anim->text_percent;
     if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 56a9f86..2eb5497 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -72,6 +72,7 @@
 #define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC)
 #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
+#define UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)
 
 #define LAST_KMSG_MAX_SZ (32 * 1024)
 
@@ -91,6 +92,7 @@
 struct charger {
     bool have_battery_state;
     bool charger_connected;
+    bool screen_blanked;
     int64_t next_screen_transition;
     int64_t next_key_check;
     int64_t next_pwr_check;
@@ -293,6 +295,7 @@
 
 #ifndef CHARGER_DISABLE_INIT_BLANK
         healthd_draw->blank_screen(true);
+        charger->screen_blanked = true;
 #endif
     }
 
@@ -301,6 +304,7 @@
         reset_animation(batt_anim);
         charger->next_screen_transition = -1;
         healthd_draw->blank_screen(true);
+        charger->screen_blanked = true;
         LOGV("[%" PRId64 "] animation done\n", now);
         if (charger->charger_connected) request_suspend(true);
         return;
@@ -308,8 +312,10 @@
 
     disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
 
-    /* unblank the screen on first cycle and first frame */
-    if (batt_anim->cur_cycle == 0 && batt_anim->cur_frame == 0) healthd_draw->blank_screen(false);
+    if (charger->screen_blanked) {
+        healthd_draw->blank_screen(false);
+        charger->screen_blanked = false;
+    }
 
     /* animation starting, set up the animation */
     if (batt_anim->cur_frame == 0) {
@@ -327,9 +333,15 @@
                     }
                 }
 
-                // repeat the first frame first_frame_repeats times
-                disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
-                            batt_anim->first_frame_repeats;
+                if (charger->charger_connected) {
+                    // repeat the first frame first_frame_repeats times
+                    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
+                                batt_anim->first_frame_repeats;
+                } else {
+                    disp_time = UNPLUGGED_DISPLAY_TIME / batt_anim->num_cycles;
+                }
+
+                LOGV("cur_frame=%d disp_time=%d\n", batt_anim->cur_frame, disp_time);
             }
         }
     }
@@ -348,7 +360,7 @@
     }
 
     /* schedule next screen transition */
-    charger->next_screen_transition = now + disp_time;
+    charger->next_screen_transition = curr_time_ms() + disp_time;
 
     /* advance frame cntr to the next valid frame only if we are charging
      * if necessary, advance cycle cntr, and reset frame cntr
@@ -458,6 +470,7 @@
             /* if the power key got released, force screen state cycle */
             if (key->pending) {
                 kick_animation(charger->batt_anim);
+                request_suspend(false);
             }
         }
     }
@@ -476,12 +489,18 @@
     if (!charger->have_battery_state) return;
 
     if (!charger->charger_connected) {
-        /* Last cycle would have stopped at the extreme top of battery-icon
-         * Need to show the correct level corresponding to capacity.
-         */
-        kick_animation(charger->batt_anim);
         request_suspend(false);
         if (charger->next_pwr_check == -1) {
+            /* Last cycle would have stopped at the extreme top of battery-icon
+             * Need to show the correct level corresponding to capacity.
+             *
+             * Reset next_screen_transition to update screen immediately.
+             * Reset & kick animation to show complete animation cycles
+             * when charger disconnected.
+             */
+            charger->next_screen_transition = now - 1;
+            reset_animation(charger->batt_anim);
+            kick_animation(charger->batt_anim);
             charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
             LOGW("[%" PRId64 "] device unplugged: shutting down in %" PRId64 " (@ %" PRId64 ")\n",
                  now, (int64_t)UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
@@ -494,8 +513,15 @@
     } else {
         /* online supply present, reset shutdown timer if set */
         if (charger->next_pwr_check != -1) {
-            LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
+            /* Reset next_screen_transition to update screen immediately.
+             * Reset & kick animation to show complete animation cycles
+             * when charger connected again.
+             */
+            request_suspend(false);
+            charger->next_screen_transition = now - 1;
+            reset_animation(charger->batt_anim);
             kick_animation(charger->batt_anim);
+            LOGW("[%" PRId64 "] device plugged in: shutdown cancelled\n", now);
         }
         charger->next_pwr_check = -1;
     }
@@ -523,6 +549,7 @@
     if (!charger->have_battery_state) {
         charger->have_battery_state = true;
         charger->next_screen_transition = curr_time_ms() - 1;
+        request_suspend(false);
         reset_animation(charger->batt_anim);
         kick_animation(charger->batt_anim);
     }
diff --git a/init/Android.bp b/init/Android.bp
index 84a78e2..c793971 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -59,34 +59,27 @@
         },
     },
     static_libs: [
-        "libbootloader_message",
-        "libfs_mgr",
-        "libfec",
-        "libfec_rs",
-        "libhidl-gen-utils",
-        "libsquashfs_utils",
-        "liblogwrap",
-        "libext4_utils",
         "libseccomp_policy",
-        "libcrypto_utils",
-        "libsparse",
         "libprocessgroup",
         "libavb",
-        "libkeyutils",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
     ],
     shared_libs: [
-        "libcutils",
         "libbase",
-        "libc",
-        "liblog",
-        "libcrypto",
-        "libc++",
+        "libbinder",
+        "libbootloader_message",
+        "libcutils",
         "libdl",
-        "libz",
+        "libext4_utils",
+        "libfs_mgr",
+        "libhidl-gen-utils",
+        "libkeyutils",
+        "liblog",
+        "liblogwrap",
         "libselinux",
+        "libutils",
     ],
 }
 
@@ -136,6 +129,13 @@
         type: "lite",
         export_proto_headers: true,
     },
+
+    target: {
+        recovery: {
+            cflags: ["-DRECOVERY"],
+            exclude_shared_libs: ["libbinder", "libutils"],
+        },
+    },
 }
 
 cc_binary {
@@ -152,6 +152,12 @@
     ],
     srcs: ["main.cpp"],
     symlinks: ["ueventd"],
+    target: {
+        recovery: {
+            cflags: ["-DRECOVERY"],
+            exclude_shared_libs: ["libbinder", "libutils"],
+        },
+    },
 }
 
 // Tests
@@ -160,6 +166,7 @@
 cc_test {
     name: "init_tests",
     defaults: ["init_defaults"],
+    compile_multilib: "first",
     srcs: [
         "devices_test.cpp",
         "init_test.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index ada87b8..5554995 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -47,6 +47,7 @@
     init_first_stage.cpp \
     reboot_utils.cpp \
     selinux.cpp \
+    switch_root.cpp \
     uevent_listener.cpp \
     util.cpp \
 
@@ -54,8 +55,15 @@
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
+
+# Set up the same mount points on the ramdisk that system-as-root contains.
+LOCAL_POST_INSTALL_CMD := \
+    mkdir -p $(TARGET_RAMDISK_OUT)/dev \
+    $(TARGET_RAMDISK_OUT)/mnt \
+    $(TARGET_RAMDISK_OUT)/proc \
+    $(TARGET_RAMDISK_OUT)/sys \
 
 LOCAL_STATIC_LIBRARIES := \
     libfs_mgr \
diff --git a/init/README.md b/init/README.md
index b0a73b9..b45da21 100644
--- a/init/README.md
+++ b/init/README.md
@@ -506,6 +506,7 @@
   _resource_ is best specified using its text representation ('cpu', 'rtio', etc
   or 'RLIM_CPU', 'RLIM_RTIO', etc). It also may be specified as the int value
   that the resource enum corresponds to.
+  _cur_ and _max_ can be 'unlimited' or '-1' to indicate an infinite rlimit.
 
 `start <service>`
 > Start a service running if it is not already running.
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 0c5cf76..1f4bec1 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -29,19 +29,22 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fs_mgr_avb.h>
+#include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
 #include <liblp/metadata_format.h>
 
 #include "devices.h"
-#include "fs_mgr.h"
-#include "fs_mgr_avb.h"
-#include "fs_mgr_dm_linear.h"
-#include "fs_mgr_overlayfs.h"
+#include "switch_root.h"
 #include "uevent.h"
 #include "uevent_listener.h"
 #include "util.h"
 
 using android::base::Timer;
 
+using namespace std::literals;
+
 namespace android {
 namespace init {
 
@@ -63,6 +66,7 @@
     bool InitRequiredDevices();
     bool InitMappedDevice(const std::string& verity_device);
     bool CreateLogicalPartitions();
+    bool MountPartition(fstab_rec* fstab_rec);
     bool MountPartitions();
     bool GetBackingDmLinearDevices();
 
@@ -116,8 +120,14 @@
     return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
 }
 
-static bool inline IsRecoveryMode() {
-    return access("/system/bin/recovery", F_OK) == 0;
+static bool IsRecoveryMode() {
+    static bool force_normal_boot = []() {
+        std::string cmdline;
+        android::base::ReadFileToString("/proc/cmdline", &cmdline);
+        return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+    }();
+
+    return !force_normal_boot && access("/system/bin/recovery", F_OK) == 0;
 }
 
 static inline bool IsDmLinearEnabled() {
@@ -333,26 +343,53 @@
     return true;
 }
 
-bool FirstStageMount::MountPartitions() {
-    for (auto fstab_rec : mount_fstab_recs_) {
-        if (fs_mgr_is_logical(fstab_rec)) {
-            if (!fs_mgr_update_logical_partition(fstab_rec)) {
-                return false;
-            }
-            if (!InitMappedDevice(fstab_rec->blk_device)) {
-                return false;
-            }
-        }
-        if (!SetUpDmVerity(fstab_rec)) {
-            PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+bool FirstStageMount::MountPartition(fstab_rec* fstab_rec) {
+    if (fs_mgr_is_logical(fstab_rec)) {
+        if (!fs_mgr_update_logical_partition(fstab_rec)) {
             return false;
         }
-        if (fs_mgr_do_mount_one(fstab_rec)) {
-            PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+        if (!InitMappedDevice(fstab_rec->blk_device)) {
             return false;
         }
     }
-    fs_mgr_overlayfs_mount_all();
+    if (!SetUpDmVerity(fstab_rec)) {
+        PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+        return false;
+    }
+    if (fs_mgr_do_mount_one(fstab_rec)) {
+        PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+        return false;
+    }
+    return true;
+}
+
+bool FirstStageMount::MountPartitions() {
+    // If system is in the fstab then we're not a system-as-root device, and in
+    // this case, we mount system first then pivot to it.  From that point on,
+    // we are effectively identical to a system-as-root device.
+    auto system_partition =
+            std::find_if(mount_fstab_recs_.begin(), mount_fstab_recs_.end(), [](const auto& rec) {
+                return rec->mount_point == "/system"s ||
+                       rec->mount_point == "/system_recovery_mount"s;
+            });
+    if (system_partition != mount_fstab_recs_.end()) {
+        if (!MountPartition(*system_partition)) {
+            return false;
+        }
+
+        SwitchRoot((*system_partition)->mount_point);
+
+        mount_fstab_recs_.erase(system_partition);
+    }
+
+    for (auto fstab_rec : mount_fstab_recs_) {
+        if (!MountPartition(fstab_rec) && !fs_mgr_is_nofail(fstab_rec)) {
+            return false;
+        }
+    }
+
+    fs_mgr_overlayfs_mount_all(device_tree_fstab_.get());
+
     return true;
 }
 
diff --git a/init/init.cpp b/init/init.cpp
index b550f1b..47cfe32 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <fcntl.h>
 #include <pthread.h>
+#include <seccomp_policy.h>
 #include <signal.h>
 #include <stdlib.h>
 #include <string.h>
@@ -40,6 +41,11 @@
 #include <cutils/android_reboot.h>
 #include <keyutils.h>
 #include <libavb/libavb.h>
+#include <selinux/android.h>
+
+#ifndef RECOVERY
+#include <binder/ProcessState.h>
+#endif
 
 #include "action_parser.h"
 #include "epoll.h"
@@ -114,8 +120,8 @@
         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("/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");
@@ -340,12 +346,12 @@
     if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
         return;
     }
-
-    std::string value = GetProperty("ro.boot.verifiedbootstate", "");
-
-    if (!value.empty()) {
-        property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
-    }
+    import_kernel_cmdline(
+            false, [](const std::string& key, const std::string& value, bool in_qemu) {
+                if (key == "androidboot.verifiedbootstate") {
+                    property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
+                }
+            });
 }
 
 static void export_kernel_boot_props() {
@@ -413,6 +419,22 @@
     return Success();
 }
 
+static Result<Success> InitBinder(const BuiltinArguments& args) {
+    // init's use of binder is very limited. init cannot:
+    //   - have any binder threads
+    //   - receive incoming binder calls
+    //   - pass local binder services to remote processes
+    //   - use death recipients
+    // The main supported usecases are:
+    //   - notifying other daemons (oneway calls only)
+    //   - retrieving data that is necessary to boot
+    // Also, binder can't be used by recovery.
+#ifndef RECOVERY
+    android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
+#endif
+    return Success();
+}
+
 // 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.
@@ -565,6 +587,43 @@
     android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
 }
 
+static void GlobalSeccomp() {
+    import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
+                                    bool in_qemu) {
+        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
+            LOG(FATAL) << "Failed to globally enable seccomp!";
+        }
+    });
+}
+
+static void SetupSelinux(char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+        RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+    });
+
+    // Set up SELinux, loading the SELinux policy.
+    SelinuxSetupKernelLogging();
+    SelinuxInitialize();
+
+    // We're in the kernel domain and want to transition to the init domain.  File systems that
+    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+    // but other file systems do.  In particular, this is needed for ramdisks such as the
+    // recovery image for A/B devices.
+    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+    }
+
+    setenv("SELINUX_INITIALIZED", "true", 1);
+
+    const char* path = "/system/bin/init";
+    const char* args[] = {path, nullptr};
+    execv(path, const_cast<char**>(args));
+
+    // execv() only returns if an error happened, in which case we
+    // panic and never return from this function.
+    PLOG(FATAL) << "execv(\"" << path << "\") failed";
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -580,9 +639,16 @@
         InstallRebootSignalHandlers();
     }
 
+    if (getenv("SELINUX_INITIALIZED") == nullptr) {
+        SetupSelinux(argv);
+    }
+
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
+    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
+    GlobalSeccomp();
+
     // Set up a session keyring that all processes will have access to. It
     // will hold things like FBE encryption keys. No process should override
     // its session keyring.
@@ -611,6 +677,7 @@
     if (avb_version) property_set("ro.boot.avb_version", avb_version);
 
     // Clean up our environment.
+    unsetenv("SELINUX_INITIALIZED");
     unsetenv("INIT_STARTED_AT");
     unsetenv("INIT_SELINUX_TOOK");
     unsetenv("INIT_AVB_VERSION");
@@ -673,6 +740,9 @@
     // wasn't ready immediately after wait_for_coldboot_done
     am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
 
+    // Initialize binder before bringing up other system services
+    am.QueueBuiltinAction(InitBinder, "InitBinder");
+
     // Don't mount filesystems or start core system services in charger mode.
     std::string bootmode = GetProperty("ro.bootmode", "");
     if (bootmode == "charger") {
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index 466cde3..0c4a110 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <paths.h>
-#include <seccomp_policy.h>
 #include <stdlib.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
@@ -30,11 +29,9 @@
 #include <android-base/logging.h>
 #include <cutils/android_reboot.h>
 #include <private/android_filesystem_config.h>
-#include <selinux/android.h>
 
 #include "first_stage_mount.h"
 #include "reboot_utils.h"
-#include "selinux.h"
 #include "util.h"
 
 using android::base::boot_clock;
@@ -42,15 +39,6 @@
 namespace android {
 namespace init {
 
-static void GlobalSeccomp() {
-    import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
-                                    bool in_qemu) {
-        if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
-            LOG(FATAL) << "Failed to globally enable seccomp!";
-        }
-    });
-}
-
 int main(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -130,22 +118,6 @@
 
     SetInitAvbVersionInRecovery();
 
-    // Does this need to be done in first stage init or can it be done later?
-    // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
-    GlobalSeccomp();
-
-    // Set up SELinux, loading the SELinux policy.
-    SelinuxSetupKernelLogging();
-    SelinuxInitialize();
-
-    // We're in the kernel domain and want to transition to the init domain when we exec second
-    // stage init.  File systems that store SELabels in their xattrs, such as ext4 do not need an
-    // explicit restorecon here, but other file systems do.  In particular, this is needed for
-    // ramdisks such as the recovery image for A/B devices.
-    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
-        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
-    }
-
     static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
     uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
     setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 1af06dd..b8c1cfd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -192,7 +192,7 @@
 
 void Keychords::GeteventOpenDevice(const std::string& device) {
     if (registration_.count(device)) return;
-    auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
+    auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDONLY | O_CLOEXEC));
     if (fd == -1) {
         PLOG(ERROR) << "Can not open " << device;
         return;
diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp
index 1e0db57..c61c210 100644
--- a/init/modalias_handler.cpp
+++ b/init/modalias_handler.cpp
@@ -50,7 +50,7 @@
     }
 
     // Key is striped module name to match names in alias file
-    std::size_t start = args[0].find_last_of("/");
+    std::size_t start = args[0].find_last_of('/');
     std::size_t end = args[0].find(".ko:");
     if ((end - start) <= 1) return Error() << "malformed dependency line";
     auto mod_name = args[0].substr(start + 1, (end - start) - 1);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index cd2f630..5c8b92a 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -692,7 +692,7 @@
         }
     }
     load_properties_from_file("/product/build.prop", NULL);
-    load_properties_from_file("/product-services/build.prop", NULL);
+    load_properties_from_file("/product_services/build.prop", NULL);
     load_properties_from_file("/odm/default.prop", NULL);
     load_properties_from_file("/vendor/default.prop", NULL);
 
diff --git a/init/property_type.cpp b/init/property_type.cpp
index 249b12b..7d80555 100644
--- a/init/property_type.cpp
+++ b/init/property_type.cpp
@@ -29,6 +29,11 @@
 namespace init {
 
 bool CheckType(const std::string& type_string, const std::string& value) {
+    // Always allow clearing a property such that the default value when it is not set takes over.
+    if (value.empty()) {
+        return true;
+    }
+
     auto type_strings = Split(type_string, " ");
     if (type_strings.empty()) {
         return false;
diff --git a/init/property_type_test.cpp b/init/property_type_test.cpp
index 068bccc..6d7f927 100644
--- a/init/property_type_test.cpp
+++ b/init/property_type_test.cpp
@@ -32,7 +32,7 @@
 }
 
 TEST(property_type, CheckType_int) {
-    EXPECT_FALSE(CheckType("int", ""));
+    EXPECT_TRUE(CheckType("int", ""));
     EXPECT_FALSE(CheckType("int", "abc"));
     EXPECT_FALSE(CheckType("int", "-abc"));
     EXPECT_TRUE(CheckType("int", "0"));
@@ -43,7 +43,7 @@
 }
 
 TEST(property_type, CheckType_uint) {
-    EXPECT_FALSE(CheckType("uint", ""));
+    EXPECT_TRUE(CheckType("uint", ""));
     EXPECT_FALSE(CheckType("uint", "abc"));
     EXPECT_FALSE(CheckType("uint", "-abc"));
     EXPECT_TRUE(CheckType("uint", "0"));
@@ -53,7 +53,7 @@
 }
 
 TEST(property_type, CheckType_double) {
-    EXPECT_FALSE(CheckType("double", ""));
+    EXPECT_TRUE(CheckType("double", ""));
     EXPECT_FALSE(CheckType("double", "abc"));
     EXPECT_FALSE(CheckType("double", "-abc"));
     EXPECT_TRUE(CheckType("double", "0.0"));
@@ -64,7 +64,7 @@
 }
 
 TEST(property_type, CheckType_size) {
-    EXPECT_FALSE(CheckType("size", ""));
+    EXPECT_TRUE(CheckType("size", ""));
     EXPECT_FALSE(CheckType("size", "ab"));
     EXPECT_FALSE(CheckType("size", "abcd"));
     EXPECT_FALSE(CheckType("size", "0"));
@@ -80,7 +80,7 @@
 }
 
 TEST(property_type, CheckType_enum) {
-    EXPECT_FALSE(CheckType("enum abc", ""));
+    EXPECT_TRUE(CheckType("enum abc", ""));
     EXPECT_FALSE(CheckType("enum abc", "ab"));
     EXPECT_FALSE(CheckType("enum abc", "abcd"));
     EXPECT_FALSE(CheckType("enum 123 456 789", "0"));
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2f88121..b84bfd3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -464,6 +464,12 @@
         cmd = ANDROID_RB_RESTART2;
         if (cmd_params.size() >= 2) {
             reboot_target = cmd_params[1];
+            // adb reboot fastboot should boot into bootloader for devices not
+            // supporting logical partitions.
+            if (reboot_target == "fastboot" &&
+                !android::base::GetBoolProperty("ro.boot.logical_partitions", false)) {
+                reboot_target = "bootloader";
+            }
             // When rebooting to the bootloader notify the bootloader writing
             // also the BCB.
             if (reboot_target == "bootloader") {
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
index fe1d6a7..1e0754a 100644
--- a/init/rlimit_parser.cpp
+++ b/init/rlimit_parser.cpp
@@ -65,12 +65,18 @@
     }
 
     rlimit limit;
-    if (!ParseUint(args[2], &limit.rlim_cur)) {
+    if (args[2] == "-1" || args[2] == "unlimited") {
+        limit.rlim_cur = RLIM_INFINITY;
+    } else if (!ParseUint(args[2], &limit.rlim_cur)) {
         return Error() << "Could not parse soft limit '" << args[2] << "'";
     }
-    if (!ParseUint(args[3], &limit.rlim_max)) {
+
+    if (args[3] == "-1" || args[3] == "unlimited") {
+        limit.rlim_max = RLIM_INFINITY;
+    } else if (!ParseUint(args[3], &limit.rlim_max)) {
         return Error() << "Could not parse hard limit '" << args[3] << "'";
     }
+
     return {resource, limit};
 }
 
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
index f3f9eb4..659ba8a 100644
--- a/init/rlimit_parser_test.cpp
+++ b/init/rlimit_parser_test.cpp
@@ -49,58 +49,58 @@
 
 TEST(rlimit, RlimitSuccess) {
     const std::vector<std::pair<std::vector<std::string>, std::pair<int, rlimit>>>
-        inputs_and_results = {
-            {{"cpu", "10", "10"}, {0, {10, 10}}},
-            {{"fsize", "10", "10"}, {1, {10, 10}}},
-            {{"data", "10", "10"}, {2, {10, 10}}},
-            {{"stack", "10", "10"}, {3, {10, 10}}},
-            {{"core", "10", "10"}, {4, {10, 10}}},
-            {{"rss", "10", "10"}, {5, {10, 10}}},
-            {{"nproc", "10", "10"}, {6, {10, 10}}},
-            {{"nofile", "10", "10"}, {7, {10, 10}}},
-            {{"memlock", "10", "10"}, {8, {10, 10}}},
-            {{"as", "10", "10"}, {9, {10, 10}}},
-            {{"locks", "10", "10"}, {10, {10, 10}}},
-            {{"sigpending", "10", "10"}, {11, {10, 10}}},
-            {{"msgqueue", "10", "10"}, {12, {10, 10}}},
-            {{"nice", "10", "10"}, {13, {10, 10}}},
-            {{"rtprio", "10", "10"}, {14, {10, 10}}},
-            {{"rttime", "10", "10"}, {15, {10, 10}}},
+            inputs_and_results = {
+                    {{"cpu", "10", "10"}, {0, {10, 10}}},
+                    {{"fsize", "10", "10"}, {1, {10, 10}}},
+                    {{"data", "10", "10"}, {2, {10, 10}}},
+                    {{"stack", "10", "10"}, {3, {10, 10}}},
+                    {{"core", "10", "10"}, {4, {10, 10}}},
+                    {{"rss", "10", "10"}, {5, {10, 10}}},
+                    {{"nproc", "10", "10"}, {6, {10, 10}}},
+                    {{"nofile", "10", "10"}, {7, {10, 10}}},
+                    {{"memlock", "10", "10"}, {8, {10, 10}}},
+                    {{"as", "10", "10"}, {9, {10, 10}}},
+                    {{"locks", "10", "10"}, {10, {10, 10}}},
+                    {{"sigpending", "10", "10"}, {11, {10, 10}}},
+                    {{"msgqueue", "10", "10"}, {12, {10, 10}}},
+                    {{"nice", "10", "10"}, {13, {10, 10}}},
+                    {{"rtprio", "10", "10"}, {14, {10, 10}}},
+                    {{"rttime", "10", "10"}, {15, {10, 10}}},
 
-            {{"RLIM_CPU", "10", "10"}, {0, {10, 10}}},
-            {{"RLIM_FSIZE", "10", "10"}, {1, {10, 10}}},
-            {{"RLIM_DATA", "10", "10"}, {2, {10, 10}}},
-            {{"RLIM_STACK", "10", "10"}, {3, {10, 10}}},
-            {{"RLIM_CORE", "10", "10"}, {4, {10, 10}}},
-            {{"RLIM_RSS", "10", "10"}, {5, {10, 10}}},
-            {{"RLIM_NPROC", "10", "10"}, {6, {10, 10}}},
-            {{"RLIM_NOFILE", "10", "10"}, {7, {10, 10}}},
-            {{"RLIM_MEMLOCK", "10", "10"}, {8, {10, 10}}},
-            {{"RLIM_AS", "10", "10"}, {9, {10, 10}}},
-            {{"RLIM_LOCKS", "10", "10"}, {10, {10, 10}}},
-            {{"RLIM_SIGPENDING", "10", "10"}, {11, {10, 10}}},
-            {{"RLIM_MSGQUEUE", "10", "10"}, {12, {10, 10}}},
-            {{"RLIM_NICE", "10", "10"}, {13, {10, 10}}},
-            {{"RLIM_RTPRIO", "10", "10"}, {14, {10, 10}}},
-            {{"RLIM_RTTIME", "10", "10"}, {15, {10, 10}}},
+                    {{"RLIM_CPU", "10", "10"}, {0, {10, 10}}},
+                    {{"RLIM_FSIZE", "10", "10"}, {1, {10, 10}}},
+                    {{"RLIM_DATA", "10", "10"}, {2, {10, 10}}},
+                    {{"RLIM_STACK", "10", "10"}, {3, {10, 10}}},
+                    {{"RLIM_CORE", "10", "10"}, {4, {10, 10}}},
+                    {{"RLIM_RSS", "10", "10"}, {5, {10, 10}}},
+                    {{"RLIM_NPROC", "10", "10"}, {6, {10, 10}}},
+                    {{"RLIM_NOFILE", "10", "10"}, {7, {10, 10}}},
+                    {{"RLIM_MEMLOCK", "10", "10"}, {8, {10, 10}}},
+                    {{"RLIM_AS", "10", "10"}, {9, {10, 10}}},
+                    {{"RLIM_LOCKS", "10", "10"}, {10, {10, 10}}},
+                    {{"RLIM_SIGPENDING", "10", "10"}, {11, {10, 10}}},
+                    {{"RLIM_MSGQUEUE", "10", "10"}, {12, {10, 10}}},
+                    {{"RLIM_NICE", "10", "10"}, {13, {10, 10}}},
+                    {{"RLIM_RTPRIO", "10", "10"}, {14, {10, 10}}},
+                    {{"RLIM_RTTIME", "10", "10"}, {15, {10, 10}}},
 
-            {{"0", "10", "10"}, {0, {10, 10}}},
-            {{"1", "10", "10"}, {1, {10, 10}}},
-            {{"2", "10", "10"}, {2, {10, 10}}},
-            {{"3", "10", "10"}, {3, {10, 10}}},
-            {{"4", "10", "10"}, {4, {10, 10}}},
-            {{"5", "10", "10"}, {5, {10, 10}}},
-            {{"6", "10", "10"}, {6, {10, 10}}},
-            {{"7", "10", "10"}, {7, {10, 10}}},
-            {{"8", "10", "10"}, {8, {10, 10}}},
-            {{"9", "10", "10"}, {9, {10, 10}}},
-            {{"10", "10", "10"}, {10, {10, 10}}},
-            {{"11", "10", "10"}, {11, {10, 10}}},
-            {{"12", "10", "10"}, {12, {10, 10}}},
-            {{"13", "10", "10"}, {13, {10, 10}}},
-            {{"14", "10", "10"}, {14, {10, 10}}},
-            {{"15", "10", "10"}, {15, {10, 10}}},
-        };
+                    {{"0", "10", "10"}, {0, {10, 10}}},
+                    {{"1", "10", "10"}, {1, {10, 10}}},
+                    {{"2", "10", "10"}, {2, {10, 10}}},
+                    {{"3", "10", "10"}, {3, {10, 10}}},
+                    {{"4", "10", "10"}, {4, {10, 10}}},
+                    {{"5", "10", "10"}, {5, {10, 10}}},
+                    {{"6", "10", "10"}, {6, {10, 10}}},
+                    {{"7", "10", "10"}, {7, {10, 10}}},
+                    {{"8", "10", "10"}, {8, {10, 10}}},
+                    {{"9", "10", "10"}, {9, {10, 10}}},
+                    {{"10", "10", "10"}, {10, {10, 10}}},
+                    {{"11", "10", "10"}, {11, {10, 10}}},
+                    {{"12", "unlimited", "10"}, {12, {RLIM_INFINITY, 10}}},
+                    {{"13", "-1", "10"}, {13, {RLIM_INFINITY, 10}}},
+                    {{"14", "10", "unlimited"}, {14, {10, RLIM_INFINITY}}},
+                    {{"15", "10", "-1"}, {15, {10, RLIM_INFINITY}}},
+            };
 
     for (const auto& [input, expected_result] : inputs_and_results) {
         TestRlimitSuccess(input, expected_result);
@@ -109,12 +109,16 @@
 
 TEST(rlimit, RlimitFailure) {
     const std::vector<std::pair<std::vector<std::string>, std::string>> inputs_and_results = {
-        {{"-4", "10", "10"}, "Resource '-4' below the minimum resource value '0'"},
-        {{"100", "10", "10"}, "Resource '100' over the maximum resource value '16'"},
-        {{"bad_string", "10", "10"}, "Could not parse resource 'bad_string'"},
-        {{"RLIM_", "10", "10"}, "Could not parse resource 'RLIM_'"},
-        {{"cpu", "abc", "10"}, "Could not parse soft limit 'abc'"},
-        {{"cpu", "10", "abc"}, "Could not parse hard limit 'abc'"},
+            {{"-4", "10", "10"}, "Resource '-4' below the minimum resource value '0'"},
+            {{"100", "10", "10"}, "Resource '100' over the maximum resource value '16'"},
+            {{"bad_string", "10", "10"}, "Could not parse resource 'bad_string'"},
+            {{"RLIM_", "10", "10"}, "Could not parse resource 'RLIM_'"},
+            {{"cpu", "abc", "10"}, "Could not parse soft limit 'abc'"},
+            {{"cpu", "10", "abc"}, "Could not parse hard limit 'abc'"},
+            {{"cpu", "unlimit", "10"}, "Could not parse soft limit 'unlimit'"},
+            {{"cpu", "10", "unlimit"}, "Could not parse hard limit 'unlimit'"},
+            {{"cpu", "-2", "10"}, "Could not parse soft limit '-2'"},
+            {{"cpu", "10", "-2"}, "Could not parse hard limit '-2'"},
     };
 
     for (const auto& [input, expected_result] : inputs_and_results) {
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index c2a21d4..092c51c 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -62,7 +62,9 @@
 Result<std::string> ReadMessage(int socket) {
     char buffer[kBufferSize] = {};
     auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
-    if (result <= 0) {
+    if (result == 0) {
+        return Error();
+    } else if (result < 0) {
         return ErrnoError();
     }
     return std::string(buffer, result);
@@ -175,6 +177,12 @@
 
         auto init_message = ReadMessage(init_fd_);
         if (!init_message) {
+            if (init_message.error_errno() == 0) {
+                // If the init file descriptor was closed, let's exit quietly. If
+                // this was accidental, init will restart us. If init died, this
+                // avoids calling abort(3) unnecessarily.
+                return;
+            }
             LOG(FATAL) << "Could not read message from init: " << init_message.error();
         }
 
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
new file mode 100644
index 0000000..0e59b57
--- /dev/null
+++ b/init/switch_root.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "switch_root.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+using android::base::StartsWith;
+
+using namespace std::literals;
+
+namespace android {
+namespace init {
+
+namespace {
+
+void FreeRamdisk(DIR* dir, dev_t dev) {
+    int dfd = dirfd(dir);
+
+    dirent* de;
+    while ((de = readdir(dir)) != nullptr) {
+        if (de->d_name == "."s || de->d_name == ".."s) {
+            continue;
+        }
+
+        bool is_dir = false;
+
+        if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
+            struct stat info;
+            if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
+                continue;
+            }
+
+            if (info.st_dev != dev) {
+                continue;
+            }
+
+            if (S_ISDIR(info.st_mode)) {
+                is_dir = true;
+                auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
+                if (fd >= 0) {
+                    auto subdir =
+                            std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};
+                    if (subdir) {
+                        FreeRamdisk(subdir.get(), dev);
+                    } else {
+                        close(fd);
+                    }
+                }
+            }
+        }
+        unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
+    }
+}
+
+std::vector<std::string> GetMounts(const std::string& new_root) {
+    auto fp = std::unique_ptr<std::FILE, decltype(&endmntent)>{setmntent("/proc/mounts", "re"),
+                                                               endmntent};
+    if (fp == nullptr) {
+        PLOG(FATAL) << "Failed to open /proc/mounts";
+    }
+
+    std::vector<std::string> result;
+    mntent* mentry;
+    while ((mentry = getmntent(fp.get())) != nullptr) {
+        // We won't try to move rootfs.
+        if (mentry->mnt_dir == "/"s) {
+            continue;
+        }
+
+        // The new root mount is handled separately.
+        if (mentry->mnt_dir == new_root) {
+            continue;
+        }
+
+        // Move operates on subtrees, so do not try to move children of other mounts.
+        if (std::find_if(result.begin(), result.end(), [&mentry](const auto& older_mount) {
+                return StartsWith(mentry->mnt_dir, older_mount);
+            }) != result.end()) {
+            continue;
+        }
+
+        result.emplace_back(mentry->mnt_dir);
+    }
+
+    return result;
+}
+
+}  // namespace
+
+void SwitchRoot(const std::string& new_root) {
+    auto mounts = GetMounts(new_root);
+
+    for (const auto& mount_path : mounts) {
+        auto new_mount_path = new_root + mount_path;
+        if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
+            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
+        }
+    }
+
+    auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
+    if (!old_root_dir) {
+        PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
+    }
+
+    struct stat old_root_info;
+    if (stat("/", &old_root_info) != 0) {
+        PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
+        old_root_dir.reset();
+    }
+
+    if (chdir(new_root.c_str()) != 0) {
+        PLOG(FATAL) << "Could not chdir to new_root, '" << new_root << "'";
+    }
+
+    if (mount(new_root.c_str(), "/", nullptr, MS_MOVE, nullptr) != 0) {
+        PLOG(FATAL) << "Unable to move root mount to new_root, '" << new_root << "'";
+    }
+
+    if (chroot(".") != 0) {
+        PLOG(FATAL) << "Unable to chroot to new root";
+    }
+
+    if (old_root_dir) {
+        FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
+    }
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/libmemunreachable/anon_vma_naming.h b/init/switch_root.h
similarity index 66%
rename from libmemunreachable/anon_vma_naming.h
rename to init/switch_root.h
index fb31e41..d515e5d 100644
--- a/libmemunreachable/anon_vma_naming.h
+++ b/init/switch_root.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
-#define LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+#pragma once
 
-#include <sys/prctl.h>
+#include <string>
 
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
+namespace android {
+namespace init {
 
-#endif  // LIBMEMUNREACHABLE_ANON_VMA_NAMING_H_
+void SwitchRoot(const std::string& new_root);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/tokenizer.cpp b/init/tokenizer.cpp
index bb143f1..7e05a0a 100644
--- a/init/tokenizer.cpp
+++ b/init/tokenizer.cpp
@@ -1,5 +1,7 @@
 #include "tokenizer.h"
 
+#include <android-base/macros.h>
+
 namespace android {
 namespace init {
 
@@ -106,6 +108,7 @@
                     continue;
                 }
                 x++;
+                FALLTHROUGH_INTENDED;
             case '\n':
                     /* \ <lf> -> line continuation */
                 state->line++;
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index a10e636..c42ae49 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -89,29 +89,20 @@
                 "libbase",
                 "liblog",
                 "libunwindstack",
-                "libdexfile",
             ],
 
             static_libs: [
                 "libprocinfo",
             ],
-
-            // libdexfile will eventually properly export headers, for now
-            // include these directly.
-            include_dirs: [
-                "art/runtime",
-            ],
         },
         android: {
             static_libs: ["libasync_safe"],
         },
         vendor: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
-            exclude_shared_libs: ["libdexfile"],
         },
         recovery: {
             cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
-            exclude_shared_libs: ["libdexfile"],
         },
     },
     whole_static_libs: ["libdemangle"],
@@ -154,7 +145,6 @@
     shared_libs: [
         "libbacktrace_test",
         "libbacktrace",
-        "libdexfile",
         "libbase",
         "liblog",
         "libunwindstack",
@@ -174,12 +164,6 @@
         },
     },
 
-    // libdexfile will eventually properly export headers, for now
-    // include these directly.
-    include_dirs: [
-        "art/runtime",
-    ],
-
     test_suites: ["device-tests"],
     data: [
         "testdata/arm/*",
diff --git a/libbacktrace/backtrace_benchmarks.cpp b/libbacktrace/backtrace_benchmarks.cpp
index 099ac60..a93a25e 100644
--- a/libbacktrace/backtrace_benchmarks.cpp
+++ b/libbacktrace/backtrace_benchmarks.cpp
@@ -35,10 +35,6 @@
 #include <backtrace/BacktraceMap.h>
 #include <unwindstack/Memory.h>
 
-// Definitions of prctl arguments to set a vma name in Android kernels.
-#define ANDROID_PR_SET_VMA 0x53564d41
-#define ANDROID_PR_SET_VMA_ANON_NAME 0
-
 constexpr size_t kNumMaps = 2000;
 
 static bool CountMaps(pid_t pid, size_t* num_maps) {
@@ -93,10 +89,11 @@
         exit(1);
       }
       memset(memory, 0x1, PAGE_SIZE);
-      if (prctl(ANDROID_PR_SET_VMA, ANDROID_PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") ==
-          -1) {
+#if defined(PR_SET_VMA)
+      if (prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, memory, PAGE_SIZE, "test_map") == -1) {
         fprintf(stderr, "Failed: %s\n", strerror(errno));
       }
+#endif
       maps.push_back(memory);
     }
 
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index af8f0a2..bd5f26f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -149,7 +149,7 @@
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_dir + 1 },
     { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_file + 1 },
     { 00600, AID_ROOT,      AID_ROOT,      0, "product/build.prop" },
-    { 00600, AID_ROOT,      AID_ROOT,      0, "product-services/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "product_services/build.prop" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump32" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/crash_dump64" },
diff --git a/libcutils/tests/sched_policy_test.cpp b/libcutils/tests/sched_policy_test.cpp
index 5942ee5..1f657e2 100644
--- a/libcutils/tests/sched_policy_test.cpp
+++ b/libcutils/tests/sched_policy_test.cpp
@@ -67,6 +67,21 @@
 }
 
 TEST(SchedPolicy, set_sched_policy) {
+    if (!schedboost_enabled()) {
+        // schedboost_enabled() (i.e. CONFIG_CGROUP_SCHEDTUNE) is optional;
+        // it's only needed on devices using energy-aware scheduler.
+        GTEST_LOG_(INFO) << "skipping test that requires CONFIG_CGROUP_SCHEDTUNE";
+        return;
+    }
+
+    ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+    AssertPolicy(SP_BACKGROUND);
+
+    ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+    AssertPolicy(SP_FOREGROUND);
+}
+
+TEST(SchedPolicy, set_sched_policy_timerslack) {
     if (!hasCapSysNice()) {
         GTEST_LOG_(INFO) << "skipping test that requires CAP_SYS_NICE";
         return;
@@ -82,11 +97,9 @@
     const unsigned int BG_FG_SLACK_FACTOR = 100;
 
     ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
-    AssertPolicy(SP_BACKGROUND);
     auto bgSleepTime = medianSleepTime();
 
     ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
-    AssertPolicy(SP_FOREGROUND);
     auto fgSleepTime = medianSleepTime();
     ASSERT_GT(bgSleepTime, fgSleepTime * BG_FG_SLACK_FACTOR);
 }
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index bcc0616..d27feb9 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
+cc_library {
     name: "libgrallocusage",
     vendor_available: true,
     cflags: [
diff --git a/libgrallocusage/OWNERS b/libgrallocusage/OWNERS
new file mode 100644
index 0000000..154dc6d
--- /dev/null
+++ b/libgrallocusage/OWNERS
@@ -0,0 +1,3 @@
+jessehall@google.com
+olv@google.com
+stoza@google.com
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index a8a9a12..383d0e7 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -30,6 +30,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #ifdef __ANDROID__  // includes sys/properties.h which does not exist outside
 #include <cutils/properties.h>
@@ -130,7 +131,7 @@
 
 static bool isLogdwActive() {
   std::string logdwSignature =
-      popenToString("grep /dev/socket/logdw /proc/net/unix");
+      popenToString("grep -a /dev/socket/logdw /proc/net/unix");
   size_t beginning = logdwSignature.find(' ');
   if (beginning == std::string::npos) return true;
   beginning = logdwSignature.find(' ', beginning + 1);
@@ -144,7 +145,7 @@
   end = logdwSignature.find(' ', end + 1);
   if (end == std::string::npos) return true;
   std::string allLogdwEndpoints = popenToString(
-      "grep ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
+      "grep -a ' 00000002" + logdwSignature.substr(beginning, end - beginning) +
       " ' /proc/net/unix | " +
       "sed -n 's/.* \\([0-9][0-9]*\\)$/ -> socket:[\\1]/p'");
   if (allLogdwEndpoints.length() == 0) return true;
@@ -2516,7 +2517,7 @@
 #endif
         elem.data.string = const_cast<char*>("<unknown>");
         elem.len = strlen(elem.data.string);
-      /* FALLTHRU */
+        FALLTHROUGH_INTENDED;
       case EVENT_TYPE_STRING:
         if (elem.len <= strOutLen) {
           memcpy(strOut, elem.data.string, elem.len);
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index 213be17..1eb7e98 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -24,6 +24,7 @@
 
 #include <sys/cdefs.h>
 #include <sys/mman.h>
+#include <sys/prctl.h>
 
 #include <cmath>
 #include <cstddef>
@@ -35,7 +36,6 @@
 
 #include "Allocator.h"
 #include "LinkedList.h"
-#include "anon_vma_naming.h"
 
 namespace android {
 
@@ -153,10 +153,10 @@
     munmap(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) + size), map_size - size);
   }
 
-#define PR_SET_VMA 0x53564d41
-#define PR_SET_VMA_ANON_NAME 0
+#if defined(PR_SET_VMA)
   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<uintptr_t>(ptr), size,
         "leak_detector_malloc");
+#endif
 
   return ptr;
 }
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index 529a043..b160de9 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -244,7 +244,7 @@
     } else if (mapping_name == "[anon:libc_malloc]") {
       // named malloc mapping
       heap_mappings.emplace_back(*it);
-    } else if (has_prefix(mapping_name, "/dev/ashmem/dalvik")) {
+    } else if (has_prefix(mapping_name, "[anon:dalvik-")) {
       // named dalvik heap mapping
       globals_mappings.emplace_back(*it);
     } else if (has_prefix(mapping_name, "[stack")) {
diff --git a/libmemunreachable/PtracerThread.cpp b/libmemunreachable/PtracerThread.cpp
index 61a1d24..d2fca49 100644
--- a/libmemunreachable/PtracerThread.cpp
+++ b/libmemunreachable/PtracerThread.cpp
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/prctl.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -32,7 +33,6 @@
 #include "android-base/macros.h"
 
 #include "PtracerThread.h"
-#include "anon_vma_naming.h"
 #include "log.h"
 
 namespace android {
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
index 9bfc935..28f1927 100644
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -99,7 +99,7 @@
 bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename);
 
 // Decrements the reference count on the dynamic library handler. If the reference count drops
-// to zero then the dynamic library is unloaded.
+// to zero then the dynamic library is unloaded. Returns 0 on success and non-zero on error.
 int NativeBridgeUnloadLibrary(void* handle);
 
 // Get last error message of native bridge when fail to load library or search symbol.
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index 19a1783..c1d9d2a 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -47,8 +47,9 @@
                         bool* needs_native_bridge,
                         std::string* error_msg);
 
-__attribute__((visibility("default")))
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge);
+__attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle,
+                                                               const bool needs_native_bridge,
+                                                               std::string* error_msg);
 
 #if defined(__ANDROID__)
 // Look up linker namespace by class_loader. Returns nullptr if
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 67c1c10..b3e2b97 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -710,9 +710,21 @@
 #endif
 }
 
-bool CloseNativeLibrary(void* handle, const bool needs_native_bridge) {
-    return needs_native_bridge ? NativeBridgeUnloadLibrary(handle) :
-                                 dlclose(handle);
+bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, std::string* error_msg) {
+  bool success;
+  if (needs_native_bridge) {
+    success = (NativeBridgeUnloadLibrary(handle) == 0);
+    if (!success) {
+      *error_msg = NativeBridgeGetError();
+    }
+  } else {
+    success = (dlclose(handle) == 0);
+    if (!success) {
+      *error_msg = dlerror();
+    }
+  }
+
+  return success;
 }
 
 #if defined(__ANDROID__)
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index c7306cd..8c80f6a 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -72,8 +72,8 @@
 LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64)
 LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) \
-		    external/safe-iop/include
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+LOCAL_HEADER_LIBRARIES := libbase_headers
 LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/libpixelflinger/arch-arm64/t32cb16blend.S b/libpixelflinger/arch-arm64/t32cb16blend.S
index b1a950d..a9733c0 100644
--- a/libpixelflinger/arch-arm64/t32cb16blend.S
+++ b/libpixelflinger/arch-arm64/t32cb16blend.S
@@ -49,7 +49,7 @@
  *      upper 16-bit pixels in DREG into FB
  *
  *
- * clobbered: w6, w7, w16, w17, w18
+ * clobbered: w6, w7, w15, w16, w17
  *
  */
 
@@ -73,8 +73,8 @@
     add     w16, w6, w16, lsr #8
     cmp     w16, #0x1F
     orr     w17, \FB, #(0x1F<<(16 + 11))
-    orr     w18, \FB, w16, lsl #(16 + 11)
-    csel    \FB, w17, w18, hi
+    orr     w15, \FB, w16, lsl #(16 + 11)
+    csel    \FB, w17, w15, hi
         // green
         and     w6, \DREG, #(0x3F<<(16 + 5))
         lsr     w17,w6,#(16+5)
@@ -84,8 +84,8 @@
         add     w6, w16, w6, lsr #8
         cmp     w6, #0x3F
         orr     w17, \FB, #(0x3F<<(16 + 5))
-        orr     w18, \FB, w6, lsl #(16 + 5)
-        csel    \FB, w17, w18, hi
+        orr     w15, \FB, w6, lsl #(16 + 5)
+        csel    \FB, w17, w15, hi
             // blue
             and     w16, \DREG, #(0x1F << 16)
             lsr     w17,w16,#16
@@ -95,8 +95,8 @@
             add     w16, w6, w16, lsr #8
             cmp     w16, #0x1F
             orr     w17, \FB, #(0x1F << 16)
-            orr     w18, \FB, w16, lsl #16
-            csel    \FB, w17, w18, hi
+            orr     w15, \FB, w16, lsl #16
+            csel    \FB, w17, w15, hi
 
 .else //Blending even pixel present in bottom 16 bits of DREG register
 
@@ -109,8 +109,8 @@
     add     w16, w6, w16, lsr #8
     cmp     w16, #0x1F
     mov     w17, #(0x1F<<11)
-    lsl     w18, w16, #11
-    csel    \FB, w17, w18, hi
+    lsl     w15, w16, #11
+    csel    \FB, w17, w15, hi
 
 
         // green
@@ -121,8 +121,8 @@
         add     w6, w16, w6, lsr #(5+8)
         cmp     w6, #0x3F
         orr     w17, \FB, #(0x3F<<5)
-        orr     w18, \FB, w6, lsl #5
-        csel    \FB, w17, w18, hi
+        orr     w15, \FB, w6, lsl #5
+        csel    \FB, w17, w15, hi
 
             // blue
             and     w16, \DREG, #0x1F
@@ -132,8 +132,8 @@
             add     w16, w6, w16, lsr #8
             cmp     w16, #0x1F
             orr     w17, \FB, #0x1F
-            orr     w18, \FB, w16
-            csel    \FB, w17, w18, hi
+            orr     w15, \FB, w16
+            csel    \FB, w17, w15, hi
 
 .endif // End of blending even pixel
 
diff --git a/libpixelflinger/buffer.cpp b/libpixelflinger/buffer.cpp
index dcb95c5..ea9514c 100644
--- a/libpixelflinger/buffer.cpp
+++ b/libpixelflinger/buffer.cpp
@@ -18,6 +18,8 @@
 
 #include <assert.h>
 
+#include <android-base/macros.h>
+
 #include "buffer.h"
 
 namespace android {
@@ -266,8 +268,11 @@
     p = downshift_component(p, b,   hbits, lbits,  f->bh, f->bl, 0, 1, -1);
     p = downshift_component(p, a,   hbits, lbits,  f->ah, f->al, 0, 1, -1);
     switch (f->size) {
-    case 1: p |= p << 8;    // fallthrough
-    case 2: p |= p << 16;
+        case 1:
+            p |= p << 8;
+            FALLTHROUGH_INTENDED;
+        case 2:
+            p |= p << 16;
     }
     return p;
 }
diff --git a/libpixelflinger/codeflinger/blending.cpp b/libpixelflinger/codeflinger/blending.cpp
index a55dfe3..2cbb00f 100644
--- a/libpixelflinger/codeflinger/blending.cpp
+++ b/libpixelflinger/codeflinger/blending.cpp
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
+#include <android-base/macros.h>
 #include <log/log.h>
 
 #include "GGLAssembler.h"
@@ -301,7 +302,7 @@
                 return;
             }                
         }
-        // fall-through...
+        FALLTHROUGH_INTENDED;
     case GGL_ONE_MINUS_DST_COLOR:
     case GGL_DST_COLOR:
     case GGL_ONE_MINUS_SRC_COLOR:
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
new file mode 100644
index 0000000..bfa730a
--- /dev/null
+++ b/libprocessgroup/OWNERS
@@ -0,0 +1,2 @@
+ccross@google.com
+tomcherry@google.com
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 1cebb5d..0a42053 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -185,7 +185,7 @@
 
     // Erase all pids that will be killed when we kill the process groups.
     for (auto it = pids.begin(); it != pids.end();) {
-        pid_t pgid = getpgid(pid);
+        pid_t pgid = getpgid(*it);
         if (pgids.count(pgid) == 1) {
             it = pids.erase(it);
         } else {
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 900fd85..4b93c5b 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -44,7 +44,7 @@
   ASSERT_EQ(maps[0].end, 0x2ac00000ULL);
   ASSERT_EQ(maps[0].flags, PROT_READ | PROT_WRITE);
   ASSERT_EQ(maps[0].pgoff, 0ULL);
-  ASSERT_EQ(maps[0].name, "/dev/ashmem/dalvik-main space (region space) (deleted)");
+  ASSERT_EQ(maps[0].name, "[anon:dalvik-main space (region space)]");
   ASSERT_EQ(maps[876].start, 0x70e6c4f000ULL);
   ASSERT_EQ(maps[876].end, 0x70e6c6b000ULL);
   ASSERT_EQ(maps[876].flags, PROT_READ | PROT_EXEC);
@@ -55,6 +55,6 @@
   ASSERT_EQ(maps[1260].flags, PROT_READ);
   ASSERT_EQ(maps[1260].pgoff, 0ULL);
   ASSERT_EQ(maps[1260].name,
-            "/dev/ashmem/dalvik-classes.dex extracted in memory from "
-            "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk (deleted)");
+            "[anon:dalvik-classes.dex extracted in memory from "
+            "/data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]");
 }
diff --git a/libprocinfo/testdata/maps b/libprocinfo/testdata/maps
index 3b312e3..098cf25 100644
--- a/libprocinfo/testdata/maps
+++ b/libprocinfo/testdata/maps
@@ -1,4 +1,4 @@
-12c00000-2ac00000 rw-p 00000000 00:05 10267643                           /dev/ashmem/dalvik-main space (region space) (deleted)
+12c00000-2ac00000 rw-p 00000000 00:05 10267643                           [anon:dalvik-main space (region space)]
 6fb5d000-6fd6e000 rw-p 00000000 103:1d 639511                            /data/dalvik-cache/arm64/system@framework@boot.art
 6fd6e000-6fd82000 r--p 00211000 103:1d 639511                            /data/dalvik-cache/arm64/system@framework@boot.art
 6fd82000-6fe47000 rw-p 00000000 103:1d 639514                            /data/dalvik-cache/arm64/system@framework@boot-core-libart.art
@@ -121,12 +121,12 @@
 73b88000-73b89000 r--s 00000000 fc:00 884                                /system/framework/boot-com.google.vr.platform.vdex
 73b89000-73b8a000 r--p 00004000 fc:00 899                                /system/framework/arm64/boot-com.google.vr.platform.oat
 73b8a000-73b8b000 rw-p 00005000 fc:00 899                                /system/framework/arm64/boot-com.google.vr.platform.oat
-73b8b000-73b93000 rw-p 00000000 00:05 10267640                           /dev/ashmem/dalvik-non moving space (deleted)
-73b93000-77b8b000 ---p 00008000 00:05 10267640                           /dev/ashmem/dalvik-non moving space (deleted)
-77b8b000-97b8b000 rw-p 00000000 00:05 10267645                           /dev/ashmem/dalvik-free list large object space (deleted)
-97b8b000-99b8b000 rw-p 00000000 00:05 10270989                           /dev/ashmem/dalvik-data-code-cache (deleted)
-99b8b000-9bb8b000 r-xp 00000000 00:05 10270990                           /dev/ashmem/dalvik-jit-code-cache (deleted)
-ebad6000-ebad7000 ---p 00000000 00:05 10269717                           /dev/ashmem/dalvik-Sentinel fault page (deleted)
+73b8b000-73b93000 rw-p 00000000 00:05 10267640                           [anon:dalvik-non moving space]
+73b93000-77b8b000 ---p 00008000 00:05 10267640                           [anon:dalvik-non moving space]
+77b8b000-97b8b000 rw-p 00000000 00:05 10267645                           [anon:dalvik-free list large object space]
+97b8b000-99b8b000 rw-p 00000000 00:05 10270989                           [anon:dalvik-data-code-cache]
+99b8b000-9bb8b000 r-xp 00000000 00:05 10270990                           [anon:dalvik-jit-code-cache]
+ebad6000-ebad7000 ---p 00000000 00:05 10269717                           [anon:dalvik-Sentinel fault page]
 7ffb6e000-7ffb76000 rw-s 000e5000 00:10 20630                            /dev/kgsl-3d0
 7ffb76000-7ffb78000 rw-s 000e0000 00:10 20630                            /dev/kgsl-3d0
 7ffbc3000-7ffbc4000 rw-s 000e8000 00:10 20630                            /dev/kgsl-3d0
@@ -453,7 +453,7 @@
 7058043000-7058044000 r--p 0002f000 fc:00 2354                           /system/lib64/libcompiler_rt.so
 7058044000-7058045000 rw-p 00030000 fc:00 2354                           /system/lib64/libcompiler_rt.so
 7058045000-70580b2000 rw-p 00000000 00:00 0                              [anon:.bss]
-70580bd000-70580dd000 rw-p 00000000 00:05 10265386                       /dev/ashmem/dalvik-LinearAlloc (deleted)
+70580bd000-70580dd000 rw-p 00000000 00:05 10265386                       [anon:dalvik-LinearAlloc]
 70580dd000-70580df000 r-xp 00000000 fc:00 2597                           /system/lib64/vndk-sp-28/libhardware.so
 70580df000-70580fc000 ---p 00000000 00:00 0 
 70580fc000-70580fd000 r--p 0000f000 fc:00 2597                           /system/lib64/vndk-sp-28/libhardware.so
@@ -568,9 +568,9 @@
 705fe4c000-705fe4d000 ---p 00000000 00:00 0                              [anon:thread stack guard]
 705fe4d000-705fe4e000 ---p 00000000 00:00 0 
 705fe4e000-705ff4a000 rw-p 00000000 00:00 0 
-705ff4a000-705ff4b000 ---p 00000000 00:05 10270991                       /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
-705ff4b000-705ff4c000 ---p 00001000 00:05 10270991                       /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
-705ff4c000-706004b000 rw-p 00002000 00:05 10270991                       /dev/ashmem/dalvik-Jit thread pool worker thread 0 (deleted)
+705ff4a000-705ff4b000 ---p 00000000 00:05 10270991                       [anon:dalvik-Jit thread pool worker thread 0]
+705ff4b000-705ff4c000 ---p 00001000 00:05 10270991                       [anon:dalvik-Jit thread pool worker thread 0]
+705ff4c000-706004b000 rw-p 00002000 00:05 10270991                       [anon:dalvik-Jit thread pool worker thread 0]
 706004b000-706010f000 r-xp 00000000 fc:00 2390                           /system/lib64/libvixl-arm64.so
 706010f000-7060120000 ---p 00000000 00:00 0 
 7060120000-7060125000 r--p 000cb000 fc:00 2390                           /system/lib64/libvixl-arm64.so
@@ -635,12 +635,12 @@
 7062003000-706201f000 ---p 00000000 00:00 0 
 706201f000-7062020000 r--p 0000f000 fc:00 2447                           /system/lib64/libtextclassifier_hash.so
 7062020000-7062021000 rw-p 00010000 fc:00 2447                           /system/lib64/libtextclassifier_hash.so
-7062022000-7062042000 rw-p 00000000 00:05 10269731                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062022000-7062042000 rw-p 00000000 00:05 10269731                       [anon:dalvik-CompilerMetadata]
 7062042000-7062077000 r-xp 00000000 fc:00 2372                           /system/lib64/android.hardware.neuralnetworks@1.0.so
 7062077000-7062095000 ---p 00000000 00:00 0 
 7062095000-706209b000 r--p 0003a000 fc:00 2372                           /system/lib64/android.hardware.neuralnetworks@1.0.so
 706209b000-706209c000 rw-p 00040000 fc:00 2372                           /system/lib64/android.hardware.neuralnetworks@1.0.so
-70620a9000-70620c9000 rw-p 00000000 00:05 10269730                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70620a9000-70620c9000 rw-p 00000000 00:05 10269730                       [anon:dalvik-CompilerMetadata]
 70620c9000-70620e3000 r-xp 00000000 fc:00 2956                           /system/lib64/android.hardware.neuralnetworks@1.1.so
 70620e3000-70620f4000 ---p 00000000 00:00 0 
 70620f4000-70620f7000 r--p 0001d000 fc:00 2956                           /system/lib64/android.hardware.neuralnetworks@1.1.so
@@ -659,7 +659,7 @@
 70623e0000-70623f7000 ---p 00000000 00:00 0 
 70623f7000-70623f8000 r--p 0001f000 fc:00 2662                           /system/lib64/libGLESv3.so
 70623f8000-70623f9000 rw-p 00020000 fc:00 2662                           /system/lib64/libGLESv3.so
-70623fc000-706241c000 rw-p 00000000 00:05 10269729                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70623fc000-706241c000 rw-p 00000000 00:05 10269729                       [anon:dalvik-CompilerMetadata]
 706241c000-7062444000 r-xp 00000000 fc:00 2603                           /system/lib64/libexif.so
 7062444000-706245f000 ---p 00000000 00:00 0 
 706245f000-7062472000 r--p 0002d000 fc:00 2603                           /system/lib64/libexif.so
@@ -674,7 +674,7 @@
 7062508000-7062522000 ---p 00000000 00:00 0 
 7062522000-7062525000 r--p 0003d000 fc:00 2401                           /system/lib64/libmtp.so
 7062525000-706252c000 rw-p 00040000 fc:00 2401                           /system/lib64/libmtp.so
-7062530000-7062550000 rw-p 00000000 00:05 10269728                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062530000-7062550000 rw-p 00000000 00:05 10269728                       [anon:dalvik-CompilerMetadata]
 7062550000-7062572000 r--s 00000000 fc:00 234                            /system/fonts/NotoSerifMyanmar-Regular.otf
 7062572000-706259e000 r-xp 00000000 fc:00 2620                           /system/lib64/libmediandk.so
 706259e000-70625b9000 ---p 00000000 00:00 0 
@@ -688,7 +688,7 @@
 7062621000-706263d000 ---p 00000000 00:00 0 
 706263d000-706263f000 r--p 0002e000 fc:00 2366                           /system/lib64/libmediadrmmetrics_lite.so
 706263f000-7062640000 rw-p 00030000 fc:00 2366                           /system/lib64/libmediadrmmetrics_lite.so
-706264b000-706266b000 rw-p 00000000 00:05 10269727                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+706264b000-706266b000 rw-p 00000000 00:05 10269727                       [anon:dalvik-CompilerMetadata]
 706266b000-70626d4000 r-xp 00000000 fc:00 2727                           /system/lib64/libmedia_jni.so
 70626d4000-70626eb000 ---p 00000000 00:00 0 
 70626eb000-70626f2000 r--p 00069000 fc:00 2727                           /system/lib64/libmedia_jni.so
@@ -697,7 +697,7 @@
 7062732000-7062748000 ---p 00000000 00:00 0 
 7062748000-706274b000 r--p 0003d000 fc:00 2399                           /system/lib64/libcamera2ndk.so
 706274b000-7062750000 rw-p 00040000 fc:00 2399                           /system/lib64/libcamera2ndk.so
-7062768000-7062788000 rw-p 00000000 00:05 10269726                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+7062768000-7062788000 rw-p 00000000 00:05 10269726                       [anon:dalvik-CompilerMetadata]
 7062788000-70627ee000 r-xp 00000000 fc:00 2974                           /system/lib64/android.hardware.drm@1.0.so
 70627ee000-7062805000 ---p 00000000 00:00 0 
 7062805000-706280d000 r--p 00068000 fc:00 2974                           /system/lib64/android.hardware.drm@1.0.so
@@ -737,17 +737,17 @@
 70629f9000-70629fc000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
 70629fc000-70629fd000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
 70629fd000-7062a3e000 r--s 00000000 fc:00 216                            /system/fonts/NotoSerif-BoldItalic.ttf
-7062a3e000-7062b06000 rw-p 00000000 00:05 10270984                       /dev/ashmem/dalvik-indirect ref table (deleted)
-7062b06000-7062bce000 rw-p 00000000 00:05 10270983                       /dev/ashmem/dalvik-indirect ref table (deleted)
-7062bce000-7062dce000 rw-p 00000000 00:05 10270726                       /dev/ashmem/dalvik-rb copying gc mark stack (deleted)
-7062dce000-70635ce000 rw-p 00000000 00:05 10270725                       /dev/ashmem/dalvik-concurrent copying gc mark stack (deleted)
-70635ce000-7063dcf000 rw-p 00000000 00:05 10270724                       /dev/ashmem/dalvik-live stack (deleted)
-7063dcf000-70645d0000 rw-p 00000000 00:05 10270723                       /dev/ashmem/dalvik-allocation stack (deleted)
-70645d0000-70649d1000 rw-p 00000000 00:05 10270721                       /dev/ashmem/dalvik-card table (deleted)
-70649d1000-7064ad1000 rw-p 00000000 00:05 10267648                       /dev/ashmem/dalvik-large object free list space allocation info map (deleted)
-7064ad1000-7065ad1000 rw-p 00000000 00:05 10267644                       /dev/ashmem/dalvik-region space live bitmap (deleted)
-7065ad1000-7065bd1000 rw-p 00000000 00:05 10267642                       /dev/ashmem/dalvik-allocspace zygote / non moving space mark-bitmap 0 (deleted)
-7065bd1000-7065cd1000 rw-p 00000000 00:05 10267641                       /dev/ashmem/dalvik-allocspace zygote / non moving space live-bitmap 0 (deleted)
+7062a3e000-7062b06000 rw-p 00000000 00:05 10270984                       [anon:dalvik-indirect ref table]
+7062b06000-7062bce000 rw-p 00000000 00:05 10270983                       [anon:dalvik-indirect ref table]
+7062bce000-7062dce000 rw-p 00000000 00:05 10270726                       [anon:dalvik-rb copying gc mark stack]
+7062dce000-70635ce000 rw-p 00000000 00:05 10270725                       [anon:dalvik-concurrent copying gc mark stack]
+70635ce000-7063dcf000 rw-p 00000000 00:05 10270724                       [anon:dalvik-live stack]
+7063dcf000-70645d0000 rw-p 00000000 00:05 10270723                       [anon:dalvik-allocation stack]
+70645d0000-70649d1000 rw-p 00000000 00:05 10270721                       [anon:dalvik-card table]
+70649d1000-7064ad1000 rw-p 00000000 00:05 10267648                       [anon:dalvik-large object free list space allocation info map]
+7064ad1000-7065ad1000 rw-p 00000000 00:05 10267644                       [anon:dalvik-region space live bitmap]
+7065ad1000-7065bd1000 rw-p 00000000 00:05 10267642                       [anon:dalvik-allocspace zygote / non moving space mark-bitmap 0]
+7065bd1000-7065cd1000 rw-p 00000000 00:05 10267641                       [anon:dalvik-allocspace zygote / non moving space live-bitmap 0]
 7065cd1000-7065cd2000 r-xp 00000000 fc:00 2946                           /system/lib64/libsigchain.so
 7065cd2000-7065cf0000 ---p 00000000 00:00 0 
 7065cf0000-7065cf1000 r--p 0000f000 fc:00 2946                           /system/lib64/libsigchain.so
@@ -769,7 +769,7 @@
 706638d000-706639e000 r--p 005ef000 fc:00 2671                           /system/lib64/libart.so
 706639e000-70663a1000 rw-p 00600000 fc:00 2671                           /system/lib64/libart.so
 70663a1000-70663a4000 rw-p 00000000 00:00 0                              [anon:.bss]
-70663a6000-70663c6000 rw-p 00000000 00:05 10269725                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70663a6000-70663c6000 rw-p 00000000 00:05 10269725                       [anon:dalvik-CompilerMetadata]
 70663c6000-70663c8000 r-xp 00000000 fc:00 2673                           /system/lib64/libmetricslogger.so
 70663c8000-70663e5000 ---p 00000000 00:00 0 
 70663e5000-70663e6000 r--p 0000f000 fc:00 2673                           /system/lib64/libmetricslogger.so
@@ -801,8 +801,8 @@
 70e688e000-70e68ab000 ---p 00000000 00:00 0 
 70e68ab000-70e68ac000 r--p 0000f000 fc:00 2943                           /system/lib64/libion.so
 70e68ac000-70e68ad000 rw-p 00010000 fc:00 2943                           /system/lib64/libion.so
-70e68ad000-70e68af000 rw-p 00000000 00:05 10282496                       /dev/ashmem/dalvik-indirect ref table (deleted)
-70e68af000-70e68b1000 rw-p 00000000 00:05 10282493                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70e68ad000-70e68af000 rw-p 00000000 00:05 10282496                       [anon:dalvik-indirect ref table]
+70e68af000-70e68b1000 rw-p 00000000 00:05 10282493                       [anon:dalvik-indirect ref table]
 70e68b1000-70e68ee000 r--s 00000000 fc:00 256                            /system/fonts/NotoSerif-Italic.ttf
 70e68ee000-70e6910000 r-xp 00000000 fc:00 2502                           /system/lib64/libhidlbase.so
 70e6910000-70e692c000 ---p 00000000 00:00 0 
@@ -821,7 +821,7 @@
 70e69e1000-70e69fa000 ---p 00000000 00:00 0 
 70e69fa000-70e69fb000 r--p 0001f000 fc:00 2537                           /system/lib64/liblog.so
 70e69fb000-70e69fc000 rw-p 00020000 fc:00 2537                           /system/lib64/liblog.so
-70e69fc000-70e69fe000 rw-p 00000000 00:05 10266158                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70e69fc000-70e69fe000 rw-p 00000000 00:05 10266158                       [anon:dalvik-indirect ref table]
 70e69fe000-70e69ff000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
 70e69ff000-70e6a02000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
 70e6a02000-70e6a03000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
@@ -854,7 +854,7 @@
 70e6b61000-70e6b73000 ---p 00000000 00:00 0 
 70e6b73000-70e6b75000 r--p 0003e000 fc:00 2695                           /system/lib64/libdexfile.so
 70e6b75000-70e6b76000 rw-p 00040000 fc:00 2695                           /system/lib64/libdexfile.so
-70e6b76000-70e6b78000 rw-p 00000000 00:05 10253452                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70e6b76000-70e6b78000 rw-p 00000000 00:05 10253452                       [anon:dalvik-indirect ref table]
 70e6b78000-70e6b85000 r--s 00000000 fc:00 1080                           /system/usr/hyphen-data/hyph-cu.hyb
 70e6b85000-70e6b96000 r-xp 00000000 fc:00 2957                           /system/lib64/libaudioutils.so
 70e6b96000-70e6bb4000 ---p 00000000 00:00 0 
@@ -1258,7 +1258,7 @@
 70e96dd000-70e96f8000 ---p 00000000 00:00 0 
 70e96f8000-70e96f9000 r--p 0000f000 fc:00 2386                           /system/lib64/libusbhost.so
 70e96f9000-70e96fa000 rw-p 00010000 fc:00 2386                           /system/lib64/libusbhost.so
-70e96fa000-70e96fb000 r--p 00000000 00:05 10266154                       /dev/ashmem/dalvik-classes.dex extracted in memory from /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk (deleted)
+70e96fa000-70e96fb000 r--p 00000000 00:05 10266154                       [anon:dalvik-classes.dex extracted in memory from /data/app/com.google.sample.tunnel-HGGRU03Gu1Mwkf_-RnFmvw==/base.apk]
 70e96fb000-70e9701000 r--s 00000000 fc:00 280                            /system/fonts/NotoSansCoptic-Regular.ttf
 70e9701000-70e9720000 r-xp 00000000 fc:00 2490                           /system/lib64/libstagefright_bufferqueue_helper.so
 70e9720000-70e973b000 ---p 00000000 00:00 0 
@@ -1291,7 +1291,7 @@
 70e98e7000-70e98e8000 rw-p 00010000 fc:00 2952                           /system/lib64/libETC1.so
 70e98e8000-70e98e9000 r--s 00000000 fc:00 1121                           /system/usr/hyphen-data/hyph-und-ethi.hyb
 70e98e9000-70e98ef000 r--s 00000000 fc:00 64                             /system/fonts/NotoSansBrahmi-Regular.ttf
-70e98ef000-70e990f000 rw-p 00000000 00:05 10271012                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e98ef000-70e990f000 rw-p 00000000 00:05 10271012                       [anon:dalvik-CompilerMetadata]
 70e990f000-70e9926000 r-xp 00000000 fc:00 2526                           /system/lib64/libbacktrace.so
 70e9926000-70e993e000 ---p 00000000 00:00 0 
 70e993e000-70e993f000 r--p 0001f000 fc:00 2526                           /system/lib64/libbacktrace.so
@@ -1304,7 +1304,7 @@
 70e99d0000-70e99d1000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70e99d1000-70e99d3000 r--s 00000000 fc:00 200                            /system/fonts/NotoSansOldItalic-Regular.ttf
 70e99d3000-70e99e1000 r--s 00000000 fc:00 153                            /system/fonts/NotoSansMalayalam-Regular.ttf
-70e99e1000-70e9a01000 rw-p 00000000 00:05 10271011                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e99e1000-70e9a01000 rw-p 00000000 00:05 10271011                       [anon:dalvik-CompilerMetadata]
 70e9a01000-70e9a1e000 r-xp 00000000 fc:00 2542                           /system/lib64/libimg_utils.so
 70e9a1e000-70e9a39000 ---p 00000000 00:00 0 
 70e9a39000-70e9a3c000 r--p 0001d000 fc:00 2542                           /system/lib64/libimg_utils.so
@@ -1330,7 +1330,7 @@
 70e9b0b000-70e9b0c000 rw-p 00030000 fc:00 2950                           /system/lib64/libmemunreachable.so
 70e9b0c000-70e9b0d000 r--s 00000000 fc:00 1088                           /system/usr/hyphen-data/hyph-ta.hyb
 70e9b0d000-70e9b0f000 r--s 00000000 fc:00 72                             /system/fonts/NotoSansOlChiki-Regular.ttf
-70e9b0f000-70e9b2f000 rw-p 00000000 00:05 10271010                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9b0f000-70e9b2f000 rw-p 00000000 00:05 10271010                       [anon:dalvik-CompilerMetadata]
 70e9b2f000-70e9b4f000 r--s 00000000 00:10 16633                          /dev/__properties__/u:object_r:persist_debug_prop:s0
 70e9b4f000-70e9b65000 r-xp 00000000 fc:00 2920                           /system/lib64/android.hardware.cas.native@1.0.so
 70e9b65000-70e9b7b000 ---p 00000000 00:00 0 
@@ -1363,13 +1363,13 @@
 70e9d01000-70e9d1e000 ---p 00000000 00:00 0 
 70e9d1e000-70e9d2e000 r--p 00080000 fc:00 2665                           /system/lib64/libbinder.so
 70e9d2e000-70e9d2f000 rw-p 00090000 fc:00 2665                           /system/lib64/libbinder.so
-70e9d2f000-70e9d4f000 rw-p 00000000 00:05 10271009                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9d2f000-70e9d4f000 rw-p 00000000 00:05 10271009                       [anon:dalvik-CompilerMetadata]
 70e9d4f000-70e9d53000 r-xp 00000000 fc:00 2454                           /system/lib64/libaudiomanager.so
 70e9d53000-70e9d6e000 ---p 00000000 00:00 0 
 70e9d6e000-70e9d6f000 r--p 0000f000 fc:00 2454                           /system/lib64/libaudiomanager.so
 70e9d6f000-70e9d70000 rw-p 00010000 fc:00 2454                           /system/lib64/libaudiomanager.so
 70e9d70000-70e9d71000 r--s 00000000 fc:00 1087                           /system/usr/hyphen-data/hyph-or.hyb
-70e9d71000-70e9d91000 rw-p 00000000 00:05 10271008                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70e9d71000-70e9d91000 rw-p 00000000 00:05 10271008                       [anon:dalvik-CompilerMetadata]
 70e9d91000-70e9e21000 r-xp 00000000 fc:00 2627                           /system/lib64/libft2.so
 70e9e21000-70e9e37000 ---p 00000000 00:00 0 
 70e9e37000-70e9e3c000 r--p 0009b000 fc:00 2627                           /system/lib64/libft2.so
@@ -1437,15 +1437,15 @@
 70eaa12000-70eaa16000 r--s 00000000 fc:00 87                             /system/fonts/NotoSansThaana-Regular.ttf
 70eaa16000-70eaa1b000 r--s 00000000 fc:00 218                            /system/fonts/NotoSansGeorgian-Bold.ttf
 70eaa1b000-70eaa20000 r--s 00000000 fc:00 125                            /system/fonts/NotoSansGeorgian-Regular.ttf
-70eaa20000-70eaa40000 rw-p 00000000 00:05 10271007                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eaa20000-70eaa40000 rw-p 00000000 00:05 10271007                       [anon:dalvik-CompilerMetadata]
 70eaa40000-70eaaa0000 r-xp 00000000 fc:00 2384                           /system/lib64/libhidltransport.so
 70eaaa0000-70eaabe000 ---p 00000000 00:00 0 
 70eaabe000-70eaac6000 r--p 00068000 fc:00 2384                           /system/lib64/libhidltransport.so
 70eaac6000-70eaac7000 rw-p 00070000 fc:00 2384                           /system/lib64/libhidltransport.so
 70eaac7000-70eaacb000 r--s 00000000 fc:00 192                            /system/fonts/NotoSerifArmenian-Bold.ttf
 70eaacb000-70eaad0000 r--s 00000000 fc:00 210                            /system/fonts/NotoSansThaiUI-Bold.ttf
-70eaad0000-70eaaf0000 rw-p 00000000 00:05 10271006                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
-70eaaf0000-70eab10000 rw-p 00000000 00:05 10271005                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eaad0000-70eaaf0000 rw-p 00000000 00:05 10271006                       [anon:dalvik-CompilerMetadata]
+70eaaf0000-70eab10000 rw-p 00000000 00:05 10271005                       [anon:dalvik-CompilerMetadata]
 70eab10000-70eab57000 r-xp 00000000 fc:00 2546                           /system/lib64/libmedia_omx.so
 70eab57000-70eab6d000 ---p 00000000 00:00 0 
 70eab6d000-70eab7a000 r--p 00053000 fc:00 2546                           /system/lib64/libmedia_omx.so
@@ -1453,7 +1453,7 @@
 70eab7f000-70eab80000 r--s 00000000 fc:00 1119                           /system/usr/hyphen-data/hyph-kn.hyb
 70eab80000-70eab86000 r--s 00000000 fc:00 224                            /system/fonts/NotoSansThaiUI-Regular.ttf
 70eab86000-70eab8b000 r--s 00000000 fc:00 300                            /system/fonts/NotoSerifThai-Bold.ttf
-70eab8b000-70eabab000 rw-p 00000000 00:05 10271004                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eab8b000-70eabab000 rw-p 00000000 00:05 10271004                       [anon:dalvik-CompilerMetadata]
 70eabab000-70eac21000 r-xp 00000000 fc:00 2385                           /system/lib64/libvintf.so
 70eac21000-70eac31000 ---p 00000000 00:00 0 
 70eac31000-70eac36000 r--p 0007b000 fc:00 2385                           /system/lib64/libvintf.so
@@ -1468,7 +1468,7 @@
 70eacd8000-70eacd9000 rw-p 00080000 fc:00 2606                           /system/lib64/android.hardware.media.omx@1.0.so
 70eacd9000-70eacdf000 r--s 00000000 fc:00 169                            /system/fonts/NotoSansThai-Regular.ttf
 70eacdf000-70eace9000 r--s 00000000 fc:00 140                            /system/fonts/CarroisGothicSC-Regular.ttf
-70eace9000-70ead09000 rw-p 00000000 00:05 10271003                       /dev/ashmem/dalvik-CompilerMetadata (deleted)
+70eace9000-70ead09000 rw-p 00000000 00:05 10271003                       [anon:dalvik-CompilerMetadata]
 70ead09000-70ead22000 r-xp 00000000 fc:00 2539                           /system/lib64/android.hardware.graphics.mapper@2.1.so
 70ead22000-70ead34000 ---p 00000000 00:00 0 
 70ead34000-70ead37000 r--p 0001d000 fc:00 2539                           /system/lib64/android.hardware.graphics.mapper@2.1.so
@@ -1503,7 +1503,7 @@
 70eae5f000-70eae62000 r--s 00000000 fc:00 103                            /system/fonts/NotoSansLimbu-Regular.ttf
 70eae62000-70eae67000 r--s 00000000 fc:00 236                            /system/fonts/NotoSansHebrew-Bold.ttf
 70eae67000-70eae84000 r--s 001c2000 fc:00 990                            /system/framework/ext.jar
-70eae84000-70eaea4000 rw-p 00000000 00:05 10269720                       /dev/ashmem/dalvik-LinearAlloc (deleted)
+70eae84000-70eaea4000 rw-p 00000000 00:05 10269720                       [anon:dalvik-LinearAlloc]
 70eaea4000-70eaede000 r-xp 00000000 fc:00 2924                           /system/lib64/libwilhelm.so
 70eaede000-70eaefa000 ---p 00000000 00:00 0 
 70eaefa000-70eaeff000 r--p 0003b000 fc:00 2924                           /system/lib64/libwilhelm.so
@@ -1579,395 +1579,395 @@
 70eb0ca000-70eb0cb000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
 70eb0cb000-70eb0ce000 rw-p 00000000 00:00 0                              [anon:bionic TLS]
 70eb0ce000-70eb0cf000 ---p 00000000 00:00 0                              [anon:bionic TLS guard]
-70eb0cf000-70eb0ef000 rw-p 00000000 00:05 10270988                       /dev/ashmem/dalvik-LinearAlloc (deleted)
+70eb0cf000-70eb0ef000 rw-p 00000000 00:05 10270988                       [anon:dalvik-LinearAlloc]
 70eb0ef000-70eb5bb000 r-xp 00000000 fc:00 2374                           /system/lib64/libpdfium.so
 70eb5bb000-70eb5cf000 ---p 00000000 00:00 0 
 70eb5cf000-70eb5e6000 r--p 004d9000 fc:00 2374                           /system/lib64/libpdfium.so
 70eb5e6000-70eb5ea000 rw-p 004f0000 fc:00 2374                           /system/lib64/libpdfium.so
 70eb5ea000-70eb5f1000 rw-p 00000000 00:00 0                              [anon:.bss]
 70eb5f1000-70eb5f2000 r--s 00000000 fc:00 1094                           /system/usr/hyphen-data/hyph-hi.hyb
-70eb5f2000-70eb5f6000 rw-p 00000000 00:05 10270982                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb5f6000-70eb5fa000 rw-p 00000000 00:05 10270981                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb5fa000-70eb5fe000 rw-p 00000000 00:05 10270980                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb5fe000-70eb602000 rw-p 00000000 00:05 10270979                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb602000-70eb606000 rw-p 00000000 00:05 10270978                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb606000-70eb60a000 rw-p 00000000 00:05 10270977                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb60a000-70eb60e000 rw-p 00000000 00:05 10270976                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb60e000-70eb612000 rw-p 00000000 00:05 10270975                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb612000-70eb616000 rw-p 00000000 00:05 10270974                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb5f2000-70eb5f6000 rw-p 00000000 00:05 10270982                       [anon:dalvik-thread local mark stack]
+70eb5f6000-70eb5fa000 rw-p 00000000 00:05 10270981                       [anon:dalvik-thread local mark stack]
+70eb5fa000-70eb5fe000 rw-p 00000000 00:05 10270980                       [anon:dalvik-thread local mark stack]
+70eb5fe000-70eb602000 rw-p 00000000 00:05 10270979                       [anon:dalvik-thread local mark stack]
+70eb602000-70eb606000 rw-p 00000000 00:05 10270978                       [anon:dalvik-thread local mark stack]
+70eb606000-70eb60a000 rw-p 00000000 00:05 10270977                       [anon:dalvik-thread local mark stack]
+70eb60a000-70eb60e000 rw-p 00000000 00:05 10270976                       [anon:dalvik-thread local mark stack]
+70eb60e000-70eb612000 rw-p 00000000 00:05 10270975                       [anon:dalvik-thread local mark stack]
+70eb612000-70eb616000 rw-p 00000000 00:05 10270974                       [anon:dalvik-thread local mark stack]
 70eb616000-70eb61a000 r-xp 00000000 fc:00 2479                           /system/lib64/libspeexresampler.so
 70eb61a000-70eb635000 ---p 00000000 00:00 0 
 70eb635000-70eb636000 r--p 0000f000 fc:00 2479                           /system/lib64/libspeexresampler.so
 70eb636000-70eb637000 rw-p 00010000 fc:00 2479                           /system/lib64/libspeexresampler.so
 70eb637000-70eb639000 r--s 00000000 fc:00 299                            /system/fonts/NotoSansImperialAramaic-Regular.ttf
-70eb639000-70eb63d000 rw-p 00000000 00:05 10270973                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb63d000-70eb641000 rw-p 00000000 00:05 10270972                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb641000-70eb645000 rw-p 00000000 00:05 10270971                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb645000-70eb649000 rw-p 00000000 00:05 10270970                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb649000-70eb64d000 rw-p 00000000 00:05 10270969                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb64d000-70eb651000 rw-p 00000000 00:05 10270968                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb651000-70eb655000 rw-p 00000000 00:05 10270967                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb655000-70eb659000 rw-p 00000000 00:05 10270966                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb659000-70eb65d000 rw-p 00000000 00:05 10270965                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb65d000-70eb661000 rw-p 00000000 00:05 10270964                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb639000-70eb63d000 rw-p 00000000 00:05 10270973                       [anon:dalvik-thread local mark stack]
+70eb63d000-70eb641000 rw-p 00000000 00:05 10270972                       [anon:dalvik-thread local mark stack]
+70eb641000-70eb645000 rw-p 00000000 00:05 10270971                       [anon:dalvik-thread local mark stack]
+70eb645000-70eb649000 rw-p 00000000 00:05 10270970                       [anon:dalvik-thread local mark stack]
+70eb649000-70eb64d000 rw-p 00000000 00:05 10270969                       [anon:dalvik-thread local mark stack]
+70eb64d000-70eb651000 rw-p 00000000 00:05 10270968                       [anon:dalvik-thread local mark stack]
+70eb651000-70eb655000 rw-p 00000000 00:05 10270967                       [anon:dalvik-thread local mark stack]
+70eb655000-70eb659000 rw-p 00000000 00:05 10270966                       [anon:dalvik-thread local mark stack]
+70eb659000-70eb65d000 rw-p 00000000 00:05 10270965                       [anon:dalvik-thread local mark stack]
+70eb65d000-70eb661000 rw-p 00000000 00:05 10270964                       [anon:dalvik-thread local mark stack]
 70eb661000-70eb6c5000 r-xp 00000000 fc:00 2461                           /system/lib64/libhidl-gen-utils.so
 70eb6c5000-70eb6df000 ---p 00000000 00:00 0 
 70eb6df000-70eb6e1000 r--p 0006e000 fc:00 2461                           /system/lib64/libhidl-gen-utils.so
 70eb6e1000-70eb6e2000 rw-p 00070000 fc:00 2461                           /system/lib64/libhidl-gen-utils.so
-70eb6e2000-70eb6e6000 rw-p 00000000 00:05 10270963                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb6e6000-70eb6ea000 rw-p 00000000 00:05 10270962                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb6ea000-70eb6ee000 rw-p 00000000 00:05 10270961                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb6ee000-70eb6f2000 rw-p 00000000 00:05 10270960                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb6f2000-70eb6f6000 rw-p 00000000 00:05 10270959                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb6f6000-70eb6fa000 rw-p 00000000 00:05 10270958                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb6fa000-70eb6fe000 rw-p 00000000 00:05 10270957                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb6fe000-70eb702000 rw-p 00000000 00:05 10270956                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb702000-70eb706000 rw-p 00000000 00:05 10270955                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb706000-70eb70a000 rw-p 00000000 00:05 10270954                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb70a000-70eb70e000 rw-p 00000000 00:05 10270953                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb70e000-70eb712000 rw-p 00000000 00:05 10270952                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb6e2000-70eb6e6000 rw-p 00000000 00:05 10270963                       [anon:dalvik-thread local mark stack]
+70eb6e6000-70eb6ea000 rw-p 00000000 00:05 10270962                       [anon:dalvik-thread local mark stack]
+70eb6ea000-70eb6ee000 rw-p 00000000 00:05 10270961                       [anon:dalvik-thread local mark stack]
+70eb6ee000-70eb6f2000 rw-p 00000000 00:05 10270960                       [anon:dalvik-thread local mark stack]
+70eb6f2000-70eb6f6000 rw-p 00000000 00:05 10270959                       [anon:dalvik-thread local mark stack]
+70eb6f6000-70eb6fa000 rw-p 00000000 00:05 10270958                       [anon:dalvik-thread local mark stack]
+70eb6fa000-70eb6fe000 rw-p 00000000 00:05 10270957                       [anon:dalvik-thread local mark stack]
+70eb6fe000-70eb702000 rw-p 00000000 00:05 10270956                       [anon:dalvik-thread local mark stack]
+70eb702000-70eb706000 rw-p 00000000 00:05 10270955                       [anon:dalvik-thread local mark stack]
+70eb706000-70eb70a000 rw-p 00000000 00:05 10270954                       [anon:dalvik-thread local mark stack]
+70eb70a000-70eb70e000 rw-p 00000000 00:05 10270953                       [anon:dalvik-thread local mark stack]
+70eb70e000-70eb712000 rw-p 00000000 00:05 10270952                       [anon:dalvik-thread local mark stack]
 70eb712000-70eb71a000 r-xp 00000000 fc:00 2652                           /system/lib64/libcamera_metadata.so
 70eb71a000-70eb72f000 ---p 00000000 00:00 0 
 70eb72f000-70eb730000 r--p 0000f000 fc:00 2652                           /system/lib64/libcamera_metadata.so
 70eb730000-70eb732000 rw-p 00010000 fc:00 2652                           /system/lib64/libcamera_metadata.so
 70eb732000-70eb734000 r--s 00000000 fc:00 131                            /system/fonts/NotoSansHanunoo-Regular.ttf
-70eb734000-70eb738000 rw-p 00000000 00:05 10270951                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb738000-70eb73c000 rw-p 00000000 00:05 10270950                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb73c000-70eb740000 rw-p 00000000 00:05 10270949                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb740000-70eb744000 rw-p 00000000 00:05 10270948                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb744000-70eb748000 rw-p 00000000 00:05 10270947                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb748000-70eb74c000 rw-p 00000000 00:05 10270946                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb74c000-70eb750000 rw-p 00000000 00:05 10270945                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb750000-70eb754000 rw-p 00000000 00:05 10270944                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb754000-70eb758000 rw-p 00000000 00:05 10270943                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb758000-70eb75c000 rw-p 00000000 00:05 10270942                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb75c000-70eb760000 rw-p 00000000 00:05 10270941                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb760000-70eb764000 rw-p 00000000 00:05 10270940                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb734000-70eb738000 rw-p 00000000 00:05 10270951                       [anon:dalvik-thread local mark stack]
+70eb738000-70eb73c000 rw-p 00000000 00:05 10270950                       [anon:dalvik-thread local mark stack]
+70eb73c000-70eb740000 rw-p 00000000 00:05 10270949                       [anon:dalvik-thread local mark stack]
+70eb740000-70eb744000 rw-p 00000000 00:05 10270948                       [anon:dalvik-thread local mark stack]
+70eb744000-70eb748000 rw-p 00000000 00:05 10270947                       [anon:dalvik-thread local mark stack]
+70eb748000-70eb74c000 rw-p 00000000 00:05 10270946                       [anon:dalvik-thread local mark stack]
+70eb74c000-70eb750000 rw-p 00000000 00:05 10270945                       [anon:dalvik-thread local mark stack]
+70eb750000-70eb754000 rw-p 00000000 00:05 10270944                       [anon:dalvik-thread local mark stack]
+70eb754000-70eb758000 rw-p 00000000 00:05 10270943                       [anon:dalvik-thread local mark stack]
+70eb758000-70eb75c000 rw-p 00000000 00:05 10270942                       [anon:dalvik-thread local mark stack]
+70eb75c000-70eb760000 rw-p 00000000 00:05 10270941                       [anon:dalvik-thread local mark stack]
+70eb760000-70eb764000 rw-p 00000000 00:05 10270940                       [anon:dalvik-thread local mark stack]
 70eb764000-70eb767000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eb767000-70eb768000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70eb768000-70eb76c000 rw-p 00000000 00:05 10270939                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb76c000-70eb770000 rw-p 00000000 00:05 10270938                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb768000-70eb76c000 rw-p 00000000 00:05 10270939                       [anon:dalvik-thread local mark stack]
+70eb76c000-70eb770000 rw-p 00000000 00:05 10270938                       [anon:dalvik-thread local mark stack]
 70eb770000-70eb771000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eb771000-70eb774000 r--s 00000000 fc:00 231                            /system/fonts/NotoSansDeseret-Regular.ttf
-70eb774000-70eb778000 rw-p 00000000 00:05 10270937                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb778000-70eb77c000 rw-p 00000000 00:05 10270936                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb77c000-70eb780000 rw-p 00000000 00:05 10270935                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb780000-70eb784000 rw-p 00000000 00:05 10270934                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb784000-70eb788000 rw-p 00000000 00:05 10270933                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb788000-70eb78c000 rw-p 00000000 00:05 10270932                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb78c000-70eb790000 rw-p 00000000 00:05 10270931                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb790000-70eb794000 rw-p 00000000 00:05 10270930                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb794000-70eb798000 rw-p 00000000 00:05 10270929                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb798000-70eb79c000 rw-p 00000000 00:05 10270928                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb79c000-70eb7a0000 rw-p 00000000 00:05 10270927                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb774000-70eb778000 rw-p 00000000 00:05 10270937                       [anon:dalvik-thread local mark stack]
+70eb778000-70eb77c000 rw-p 00000000 00:05 10270936                       [anon:dalvik-thread local mark stack]
+70eb77c000-70eb780000 rw-p 00000000 00:05 10270935                       [anon:dalvik-thread local mark stack]
+70eb780000-70eb784000 rw-p 00000000 00:05 10270934                       [anon:dalvik-thread local mark stack]
+70eb784000-70eb788000 rw-p 00000000 00:05 10270933                       [anon:dalvik-thread local mark stack]
+70eb788000-70eb78c000 rw-p 00000000 00:05 10270932                       [anon:dalvik-thread local mark stack]
+70eb78c000-70eb790000 rw-p 00000000 00:05 10270931                       [anon:dalvik-thread local mark stack]
+70eb790000-70eb794000 rw-p 00000000 00:05 10270930                       [anon:dalvik-thread local mark stack]
+70eb794000-70eb798000 rw-p 00000000 00:05 10270929                       [anon:dalvik-thread local mark stack]
+70eb798000-70eb79c000 rw-p 00000000 00:05 10270928                       [anon:dalvik-thread local mark stack]
+70eb79c000-70eb7a0000 rw-p 00000000 00:05 10270927                       [anon:dalvik-thread local mark stack]
 70eb7a0000-70eb7a1000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb7a1000-70eb7a3000 r--s 00000000 fc:00 176                            /system/fonts/NotoSansGothic-Regular.ttf
-70eb7a3000-70eb7a7000 rw-p 00000000 00:05 10270926                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7a3000-70eb7a7000 rw-p 00000000 00:05 10270926                       [anon:dalvik-thread local mark stack]
 70eb7a7000-70eb7a8000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb7a8000-70eb7a9000 r--s 00000000 fc:00 1109                           /system/usr/hyphen-data/hyph-gu.hyb
-70eb7a9000-70eb7ad000 rw-p 00000000 00:05 10270925                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7a9000-70eb7ad000 rw-p 00000000 00:05 10270925                       [anon:dalvik-thread local mark stack]
 70eb7ad000-70eb7ae000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb7ae000-70eb7af000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eb7af000-70eb7b0000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb7b0000-70eb7b2000 r--s 00000000 fc:00 191                            /system/fonts/NotoSansCypriot-Regular.ttf
-70eb7b2000-70eb7b6000 rw-p 00000000 00:05 10270924                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7b6000-70eb7ba000 rw-p 00000000 00:05 10270923                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7ba000-70eb7be000 rw-p 00000000 00:05 10270922                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7be000-70eb7c2000 rw-p 00000000 00:05 10270921                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7c2000-70eb7c6000 rw-p 00000000 00:05 10270920                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7c6000-70eb7ca000 rw-p 00000000 00:05 10270919                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7ca000-70eb7ce000 rw-p 00000000 00:05 10270918                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7ce000-70eb7d2000 rw-p 00000000 00:05 10270917                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7d2000-70eb7d6000 rw-p 00000000 00:05 10270916                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7b2000-70eb7b6000 rw-p 00000000 00:05 10270924                       [anon:dalvik-thread local mark stack]
+70eb7b6000-70eb7ba000 rw-p 00000000 00:05 10270923                       [anon:dalvik-thread local mark stack]
+70eb7ba000-70eb7be000 rw-p 00000000 00:05 10270922                       [anon:dalvik-thread local mark stack]
+70eb7be000-70eb7c2000 rw-p 00000000 00:05 10270921                       [anon:dalvik-thread local mark stack]
+70eb7c2000-70eb7c6000 rw-p 00000000 00:05 10270920                       [anon:dalvik-thread local mark stack]
+70eb7c6000-70eb7ca000 rw-p 00000000 00:05 10270919                       [anon:dalvik-thread local mark stack]
+70eb7ca000-70eb7ce000 rw-p 00000000 00:05 10270918                       [anon:dalvik-thread local mark stack]
+70eb7ce000-70eb7d2000 rw-p 00000000 00:05 10270917                       [anon:dalvik-thread local mark stack]
+70eb7d2000-70eb7d6000 rw-p 00000000 00:05 10270916                       [anon:dalvik-thread local mark stack]
 70eb7d6000-70eb7d7000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
-70eb7d7000-70eb7db000 rw-p 00000000 00:05 10270915                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7db000-70eb7df000 rw-p 00000000 00:05 10270914                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7df000-70eb7e3000 rw-p 00000000 00:05 10270913                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7e3000-70eb7e7000 rw-p 00000000 00:05 10270912                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7d7000-70eb7db000 rw-p 00000000 00:05 10270915                       [anon:dalvik-thread local mark stack]
+70eb7db000-70eb7df000 rw-p 00000000 00:05 10270914                       [anon:dalvik-thread local mark stack]
+70eb7df000-70eb7e3000 rw-p 00000000 00:05 10270913                       [anon:dalvik-thread local mark stack]
+70eb7e3000-70eb7e7000 rw-p 00000000 00:05 10270912                       [anon:dalvik-thread local mark stack]
 70eb7e7000-70eb7e8000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb7e8000-70eb7ea000 r--s 00000000 fc:00 174                            /system/fonts/NotoSansCarian-Regular.ttf
-70eb7ea000-70eb7ee000 rw-p 00000000 00:05 10270911                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7ee000-70eb7f2000 rw-p 00000000 00:05 10270910                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7f2000-70eb7f6000 rw-p 00000000 00:05 10270909                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7ea000-70eb7ee000 rw-p 00000000 00:05 10270911                       [anon:dalvik-thread local mark stack]
+70eb7ee000-70eb7f2000 rw-p 00000000 00:05 10270910                       [anon:dalvik-thread local mark stack]
+70eb7f2000-70eb7f6000 rw-p 00000000 00:05 10270909                       [anon:dalvik-thread local mark stack]
 70eb7f6000-70eb7f7000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eb7f7000-70eb7f8000 r--s 00000000 fc:00 1096                           /system/usr/hyphen-data/hyph-eu.hyb
-70eb7f8000-70eb7fc000 rw-p 00000000 00:05 10270908                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb7fc000-70eb800000 rw-p 00000000 00:05 10270907                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb800000-70eb804000 rw-p 00000000 00:05 10270906                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb804000-70eb808000 rw-p 00000000 00:05 10270905                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb808000-70eb80c000 rw-p 00000000 00:05 10270904                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb80c000-70eb810000 rw-p 00000000 00:05 10270903                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb810000-70eb814000 rw-p 00000000 00:05 10270902                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb7f8000-70eb7fc000 rw-p 00000000 00:05 10270908                       [anon:dalvik-thread local mark stack]
+70eb7fc000-70eb800000 rw-p 00000000 00:05 10270907                       [anon:dalvik-thread local mark stack]
+70eb800000-70eb804000 rw-p 00000000 00:05 10270906                       [anon:dalvik-thread local mark stack]
+70eb804000-70eb808000 rw-p 00000000 00:05 10270905                       [anon:dalvik-thread local mark stack]
+70eb808000-70eb80c000 rw-p 00000000 00:05 10270904                       [anon:dalvik-thread local mark stack]
+70eb80c000-70eb810000 rw-p 00000000 00:05 10270903                       [anon:dalvik-thread local mark stack]
+70eb810000-70eb814000 rw-p 00000000 00:05 10270902                       [anon:dalvik-thread local mark stack]
 70eb814000-70eb815000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
-70eb815000-70eb819000 rw-p 00000000 00:05 10270901                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb819000-70eb81d000 rw-p 00000000 00:05 10270900                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb815000-70eb819000 rw-p 00000000 00:05 10270901                       [anon:dalvik-thread local mark stack]
+70eb819000-70eb81d000 rw-p 00000000 00:05 10270900                       [anon:dalvik-thread local mark stack]
 70eb81d000-70eb81e000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70eb81e000-70eb822000 rw-p 00000000 00:05 10270899                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb822000-70eb826000 rw-p 00000000 00:05 10270898                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb826000-70eb82a000 rw-p 00000000 00:05 10270897                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb82a000-70eb82e000 rw-p 00000000 00:05 10270896                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb82e000-70eb832000 rw-p 00000000 00:05 10270895                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb832000-70eb836000 rw-p 00000000 00:05 10270894                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb81e000-70eb822000 rw-p 00000000 00:05 10270899                       [anon:dalvik-thread local mark stack]
+70eb822000-70eb826000 rw-p 00000000 00:05 10270898                       [anon:dalvik-thread local mark stack]
+70eb826000-70eb82a000 rw-p 00000000 00:05 10270897                       [anon:dalvik-thread local mark stack]
+70eb82a000-70eb82e000 rw-p 00000000 00:05 10270896                       [anon:dalvik-thread local mark stack]
+70eb82e000-70eb832000 rw-p 00000000 00:05 10270895                       [anon:dalvik-thread local mark stack]
+70eb832000-70eb836000 rw-p 00000000 00:05 10270894                       [anon:dalvik-thread local mark stack]
 70eb836000-70eb837000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70eb837000-70eb83b000 rw-p 00000000 00:05 10270893                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb83b000-70eb83f000 rw-p 00000000 00:05 10270892                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb83f000-70eb843000 rw-p 00000000 00:05 10270891                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb843000-70eb847000 rw-p 00000000 00:05 10270890                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb847000-70eb84b000 rw-p 00000000 00:05 10270889                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb84b000-70eb84f000 rw-p 00000000 00:05 10270888                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb837000-70eb83b000 rw-p 00000000 00:05 10270893                       [anon:dalvik-thread local mark stack]
+70eb83b000-70eb83f000 rw-p 00000000 00:05 10270892                       [anon:dalvik-thread local mark stack]
+70eb83f000-70eb843000 rw-p 00000000 00:05 10270891                       [anon:dalvik-thread local mark stack]
+70eb843000-70eb847000 rw-p 00000000 00:05 10270890                       [anon:dalvik-thread local mark stack]
+70eb847000-70eb84b000 rw-p 00000000 00:05 10270889                       [anon:dalvik-thread local mark stack]
+70eb84b000-70eb84f000 rw-p 00000000 00:05 10270888                       [anon:dalvik-thread local mark stack]
 70eb84f000-70eb850000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70eb850000-70eb854000 rw-p 00000000 00:05 10270887                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb854000-70eb858000 rw-p 00000000 00:05 10270886                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb858000-70eb85c000 rw-p 00000000 00:05 10270885                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb85c000-70eb860000 rw-p 00000000 00:05 10270884                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb860000-70eb864000 rw-p 00000000 00:05 10270883                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb864000-70eb868000 rw-p 00000000 00:05 10270882                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb850000-70eb854000 rw-p 00000000 00:05 10270887                       [anon:dalvik-thread local mark stack]
+70eb854000-70eb858000 rw-p 00000000 00:05 10270886                       [anon:dalvik-thread local mark stack]
+70eb858000-70eb85c000 rw-p 00000000 00:05 10270885                       [anon:dalvik-thread local mark stack]
+70eb85c000-70eb860000 rw-p 00000000 00:05 10270884                       [anon:dalvik-thread local mark stack]
+70eb860000-70eb864000 rw-p 00000000 00:05 10270883                       [anon:dalvik-thread local mark stack]
+70eb864000-70eb868000 rw-p 00000000 00:05 10270882                       [anon:dalvik-thread local mark stack]
 70eb868000-70eb869000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70eb869000-70eb86d000 rw-p 00000000 00:05 10270881                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb86d000-70eb871000 rw-p 00000000 00:05 10270880                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb871000-70eb875000 rw-p 00000000 00:05 10270879                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb875000-70eb879000 rw-p 00000000 00:05 10270878                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb879000-70eb87d000 rw-p 00000000 00:05 10270877                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb87d000-70eb881000 rw-p 00000000 00:05 10270876                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb881000-70eb885000 rw-p 00000000 00:05 10270875                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb885000-70eb889000 rw-p 00000000 00:05 10270874                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb889000-70eb88d000 rw-p 00000000 00:05 10270873                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb88d000-70eb891000 rw-p 00000000 00:05 10270872                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb869000-70eb86d000 rw-p 00000000 00:05 10270881                       [anon:dalvik-thread local mark stack]
+70eb86d000-70eb871000 rw-p 00000000 00:05 10270880                       [anon:dalvik-thread local mark stack]
+70eb871000-70eb875000 rw-p 00000000 00:05 10270879                       [anon:dalvik-thread local mark stack]
+70eb875000-70eb879000 rw-p 00000000 00:05 10270878                       [anon:dalvik-thread local mark stack]
+70eb879000-70eb87d000 rw-p 00000000 00:05 10270877                       [anon:dalvik-thread local mark stack]
+70eb87d000-70eb881000 rw-p 00000000 00:05 10270876                       [anon:dalvik-thread local mark stack]
+70eb881000-70eb885000 rw-p 00000000 00:05 10270875                       [anon:dalvik-thread local mark stack]
+70eb885000-70eb889000 rw-p 00000000 00:05 10270874                       [anon:dalvik-thread local mark stack]
+70eb889000-70eb88d000 rw-p 00000000 00:05 10270873                       [anon:dalvik-thread local mark stack]
+70eb88d000-70eb891000 rw-p 00000000 00:05 10270872                       [anon:dalvik-thread local mark stack]
 70eb891000-70eb892000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70eb892000-70eb896000 rw-p 00000000 00:05 10270871                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb896000-70eb89a000 rw-p 00000000 00:05 10270870                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb892000-70eb896000 rw-p 00000000 00:05 10270871                       [anon:dalvik-thread local mark stack]
+70eb896000-70eb89a000 rw-p 00000000 00:05 10270870                       [anon:dalvik-thread local mark stack]
 70eb89a000-70eb89b000 rw-p 00000000 00:00 0                              [anon:linker_alloc_vector]
 70eb89b000-70eb89c000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
-70eb89c000-70eb8a0000 rw-p 00000000 00:05 10270869                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8a0000-70eb8a4000 rw-p 00000000 00:05 10270868                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb89c000-70eb8a0000 rw-p 00000000 00:05 10270869                       [anon:dalvik-thread local mark stack]
+70eb8a0000-70eb8a4000 rw-p 00000000 00:05 10270868                       [anon:dalvik-thread local mark stack]
 70eb8a4000-70eb8a5000 r--p 00000000 00:00 0                              [anon:atexit handlers]
-70eb8a5000-70eb8a9000 rw-p 00000000 00:05 10270867                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8a9000-70eb8ad000 rw-p 00000000 00:05 10270866                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8ad000-70eb8b1000 rw-p 00000000 00:05 10270865                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8b1000-70eb8b5000 rw-p 00000000 00:05 10270864                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8b5000-70eb8b9000 rw-p 00000000 00:05 10270863                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8b9000-70eb8bd000 rw-p 00000000 00:05 10270862                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8a5000-70eb8a9000 rw-p 00000000 00:05 10270867                       [anon:dalvik-thread local mark stack]
+70eb8a9000-70eb8ad000 rw-p 00000000 00:05 10270866                       [anon:dalvik-thread local mark stack]
+70eb8ad000-70eb8b1000 rw-p 00000000 00:05 10270865                       [anon:dalvik-thread local mark stack]
+70eb8b1000-70eb8b5000 rw-p 00000000 00:05 10270864                       [anon:dalvik-thread local mark stack]
+70eb8b5000-70eb8b9000 rw-p 00000000 00:05 10270863                       [anon:dalvik-thread local mark stack]
+70eb8b9000-70eb8bd000 rw-p 00000000 00:05 10270862                       [anon:dalvik-thread local mark stack]
 70eb8bd000-70eb8be000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eb8be000-70eb8c1000 r--s 00000000 fc:00 168                            /system/fonts/NotoSansAvestan-Regular.ttf
-70eb8c1000-70eb8c5000 rw-p 00000000 00:05 10270861                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8c5000-70eb8c9000 rw-p 00000000 00:05 10270860                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8c9000-70eb8cd000 rw-p 00000000 00:05 10270859                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8cd000-70eb8d1000 rw-p 00000000 00:05 10270858                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8d1000-70eb8d5000 rw-p 00000000 00:05 10270857                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8c1000-70eb8c5000 rw-p 00000000 00:05 10270861                       [anon:dalvik-thread local mark stack]
+70eb8c5000-70eb8c9000 rw-p 00000000 00:05 10270860                       [anon:dalvik-thread local mark stack]
+70eb8c9000-70eb8cd000 rw-p 00000000 00:05 10270859                       [anon:dalvik-thread local mark stack]
+70eb8cd000-70eb8d1000 rw-p 00000000 00:05 10270858                       [anon:dalvik-thread local mark stack]
+70eb8d1000-70eb8d5000 rw-p 00000000 00:05 10270857                       [anon:dalvik-thread local mark stack]
 70eb8d5000-70eb8d7000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70eb8d7000-70eb8db000 rw-p 00000000 00:05 10270856                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8db000-70eb8df000 rw-p 00000000 00:05 10270855                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8df000-70eb8e3000 rw-p 00000000 00:05 10270854                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8e3000-70eb8e7000 rw-p 00000000 00:05 10270853                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8e7000-70eb8eb000 rw-p 00000000 00:05 10270852                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8d7000-70eb8db000 rw-p 00000000 00:05 10270856                       [anon:dalvik-thread local mark stack]
+70eb8db000-70eb8df000 rw-p 00000000 00:05 10270855                       [anon:dalvik-thread local mark stack]
+70eb8df000-70eb8e3000 rw-p 00000000 00:05 10270854                       [anon:dalvik-thread local mark stack]
+70eb8e3000-70eb8e7000 rw-p 00000000 00:05 10270853                       [anon:dalvik-thread local mark stack]
+70eb8e7000-70eb8eb000 rw-p 00000000 00:05 10270852                       [anon:dalvik-thread local mark stack]
 70eb8eb000-70eb8ec000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb8ec000-70eb8ed000 r--s 00000000 fc:00 1099                           /system/usr/hyphen-data/hyph-bn.hyb
-70eb8ed000-70eb8f1000 rw-p 00000000 00:05 10270851                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8f1000-70eb8f5000 rw-p 00000000 00:05 10270850                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8f5000-70eb8f9000 rw-p 00000000 00:05 10270849                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8f9000-70eb8fd000 rw-p 00000000 00:05 10270848                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb8fd000-70eb901000 rw-p 00000000 00:05 10270847                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb901000-70eb905000 rw-p 00000000 00:05 10270846                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb905000-70eb909000 rw-p 00000000 00:05 10270845                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb909000-70eb90d000 rw-p 00000000 00:05 10270844                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb90d000-70eb911000 rw-p 00000000 00:05 10270843                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb911000-70eb915000 rw-p 00000000 00:05 10270842                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb8ed000-70eb8f1000 rw-p 00000000 00:05 10270851                       [anon:dalvik-thread local mark stack]
+70eb8f1000-70eb8f5000 rw-p 00000000 00:05 10270850                       [anon:dalvik-thread local mark stack]
+70eb8f5000-70eb8f9000 rw-p 00000000 00:05 10270849                       [anon:dalvik-thread local mark stack]
+70eb8f9000-70eb8fd000 rw-p 00000000 00:05 10270848                       [anon:dalvik-thread local mark stack]
+70eb8fd000-70eb901000 rw-p 00000000 00:05 10270847                       [anon:dalvik-thread local mark stack]
+70eb901000-70eb905000 rw-p 00000000 00:05 10270846                       [anon:dalvik-thread local mark stack]
+70eb905000-70eb909000 rw-p 00000000 00:05 10270845                       [anon:dalvik-thread local mark stack]
+70eb909000-70eb90d000 rw-p 00000000 00:05 10270844                       [anon:dalvik-thread local mark stack]
+70eb90d000-70eb911000 rw-p 00000000 00:05 10270843                       [anon:dalvik-thread local mark stack]
+70eb911000-70eb915000 rw-p 00000000 00:05 10270842                       [anon:dalvik-thread local mark stack]
 70eb915000-70eb916000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eb916000-70eb917000 r--s 00000000 fc:00 1114                           /system/usr/hyphen-data/hyph-bg.hyb
-70eb917000-70eb91b000 rw-p 00000000 00:05 10270841                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb917000-70eb91b000 rw-p 00000000 00:05 10270841                       [anon:dalvik-thread local mark stack]
 70eb91b000-70eb91c000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb91c000-70eb91d000 r--s 00000000 fc:00 1133                           /system/usr/hyphen-data/hyph-as.hyb
-70eb91d000-70eb921000 rw-p 00000000 00:05 10270840                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb921000-70eb925000 rw-p 00000000 00:05 10270839                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb91d000-70eb921000 rw-p 00000000 00:05 10270840                       [anon:dalvik-thread local mark stack]
+70eb921000-70eb925000 rw-p 00000000 00:05 10270839                       [anon:dalvik-thread local mark stack]
 70eb925000-70eb926000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
 70eb926000-70eb927000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb927000-70eb929000 r--s 00000000 fc:00 203                            /system/fonts/NotoSansBuhid-Regular.ttf
-70eb929000-70eb92d000 rw-p 00000000 00:05 10270838                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb92d000-70eb931000 rw-p 00000000 00:05 10270837                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb931000-70eb935000 rw-p 00000000 00:05 10270836                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb935000-70eb939000 rw-p 00000000 00:05 10270835                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb939000-70eb93d000 rw-p 00000000 00:05 10270834                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb93d000-70eb941000 rw-p 00000000 00:05 10270833                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb941000-70eb945000 rw-p 00000000 00:05 10270832                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb945000-70eb949000 rw-p 00000000 00:05 10270831                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb949000-70eb94d000 rw-p 00000000 00:05 10270830                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb94d000-70eb951000 rw-p 00000000 00:05 10270829                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb951000-70eb991000 rw-p 00000000 00:05 10270722                       /dev/ashmem/dalvik-mark stack (deleted)
+70eb929000-70eb92d000 rw-p 00000000 00:05 10270838                       [anon:dalvik-thread local mark stack]
+70eb92d000-70eb931000 rw-p 00000000 00:05 10270837                       [anon:dalvik-thread local mark stack]
+70eb931000-70eb935000 rw-p 00000000 00:05 10270836                       [anon:dalvik-thread local mark stack]
+70eb935000-70eb939000 rw-p 00000000 00:05 10270835                       [anon:dalvik-thread local mark stack]
+70eb939000-70eb93d000 rw-p 00000000 00:05 10270834                       [anon:dalvik-thread local mark stack]
+70eb93d000-70eb941000 rw-p 00000000 00:05 10270833                       [anon:dalvik-thread local mark stack]
+70eb941000-70eb945000 rw-p 00000000 00:05 10270832                       [anon:dalvik-thread local mark stack]
+70eb945000-70eb949000 rw-p 00000000 00:05 10270831                       [anon:dalvik-thread local mark stack]
+70eb949000-70eb94d000 rw-p 00000000 00:05 10270830                       [anon:dalvik-thread local mark stack]
+70eb94d000-70eb951000 rw-p 00000000 00:05 10270829                       [anon:dalvik-thread local mark stack]
+70eb951000-70eb991000 rw-p 00000000 00:05 10270722                       [anon:dalvik-mark stack]
 70eb991000-70eb992000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70eb992000-70eb996000 rw-p 00000000 00:05 10270828                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb996000-70eb99a000 rw-p 00000000 00:05 10270827                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb99a000-70eb99e000 rw-p 00000000 00:05 10270826                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb99e000-70eb9a2000 rw-p 00000000 00:05 10270825                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb992000-70eb996000 rw-p 00000000 00:05 10270828                       [anon:dalvik-thread local mark stack]
+70eb996000-70eb99a000 rw-p 00000000 00:05 10270827                       [anon:dalvik-thread local mark stack]
+70eb99a000-70eb99e000 rw-p 00000000 00:05 10270826                       [anon:dalvik-thread local mark stack]
+70eb99e000-70eb9a2000 rw-p 00000000 00:05 10270825                       [anon:dalvik-thread local mark stack]
 70eb9a2000-70eb9a4000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70eb9a4000-70eb9a8000 rw-p 00000000 00:05 10270824                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9a8000-70eb9ac000 rw-p 00000000 00:05 10270823                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9ac000-70eb9b0000 rw-p 00000000 00:05 10270822                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9a4000-70eb9a8000 rw-p 00000000 00:05 10270824                       [anon:dalvik-thread local mark stack]
+70eb9a8000-70eb9ac000 rw-p 00000000 00:05 10270823                       [anon:dalvik-thread local mark stack]
+70eb9ac000-70eb9b0000 rw-p 00000000 00:05 10270822                       [anon:dalvik-thread local mark stack]
 70eb9b0000-70eb9b1000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eb9b1000-70eb9b2000 r--s 00021000 fc:01 1180                           /vendor/overlay/framework-res__auto_generated_rro.apk
-70eb9b2000-70eb9b6000 rw-p 00000000 00:05 10270821                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9b6000-70eb9ba000 rw-p 00000000 00:05 10270820                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9ba000-70eb9be000 rw-p 00000000 00:05 10270819                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9be000-70eb9c2000 rw-p 00000000 00:05 10270818                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9c2000-70eb9c6000 rw-p 00000000 00:05 10270817                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9c6000-70eb9ca000 rw-p 00000000 00:05 10270816                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9ca000-70eb9ce000 rw-p 00000000 00:05 10270815                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9b2000-70eb9b6000 rw-p 00000000 00:05 10270821                       [anon:dalvik-thread local mark stack]
+70eb9b6000-70eb9ba000 rw-p 00000000 00:05 10270820                       [anon:dalvik-thread local mark stack]
+70eb9ba000-70eb9be000 rw-p 00000000 00:05 10270819                       [anon:dalvik-thread local mark stack]
+70eb9be000-70eb9c2000 rw-p 00000000 00:05 10270818                       [anon:dalvik-thread local mark stack]
+70eb9c2000-70eb9c6000 rw-p 00000000 00:05 10270817                       [anon:dalvik-thread local mark stack]
+70eb9c6000-70eb9ca000 rw-p 00000000 00:05 10270816                       [anon:dalvik-thread local mark stack]
+70eb9ca000-70eb9ce000 rw-p 00000000 00:05 10270815                       [anon:dalvik-thread local mark stack]
 70eb9ce000-70eb9cf000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eb9cf000-70eb9d1000 r--s 00000000 fc:00 213                            /system/fonts/NotoSansBuginese-Regular.ttf
-70eb9d1000-70eb9d5000 rw-p 00000000 00:05 10270814                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9d5000-70eb9d9000 rw-p 00000000 00:05 10270813                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9d9000-70eb9dd000 rw-p 00000000 00:05 10270812                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9dd000-70eb9e1000 rw-p 00000000 00:05 10270811                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9e1000-70eb9e5000 rw-p 00000000 00:05 10270810                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9e5000-70eb9e9000 rw-p 00000000 00:05 10270809                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9e9000-70eb9ed000 rw-p 00000000 00:05 10270808                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9ed000-70eb9f1000 rw-p 00000000 00:05 10270807                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eb9f1000-70eb9f5000 rw-p 00000000 00:05 10270806                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9d1000-70eb9d5000 rw-p 00000000 00:05 10270814                       [anon:dalvik-thread local mark stack]
+70eb9d5000-70eb9d9000 rw-p 00000000 00:05 10270813                       [anon:dalvik-thread local mark stack]
+70eb9d9000-70eb9dd000 rw-p 00000000 00:05 10270812                       [anon:dalvik-thread local mark stack]
+70eb9dd000-70eb9e1000 rw-p 00000000 00:05 10270811                       [anon:dalvik-thread local mark stack]
+70eb9e1000-70eb9e5000 rw-p 00000000 00:05 10270810                       [anon:dalvik-thread local mark stack]
+70eb9e5000-70eb9e9000 rw-p 00000000 00:05 10270809                       [anon:dalvik-thread local mark stack]
+70eb9e9000-70eb9ed000 rw-p 00000000 00:05 10270808                       [anon:dalvik-thread local mark stack]
+70eb9ed000-70eb9f1000 rw-p 00000000 00:05 10270807                       [anon:dalvik-thread local mark stack]
+70eb9f1000-70eb9f5000 rw-p 00000000 00:05 10270806                       [anon:dalvik-thread local mark stack]
 70eb9f5000-70eb9f6000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70eb9f6000-70eb9f8000 rw-p 00000000 00:05 10271002                       /dev/ashmem/dalvik-indirect ref table (deleted)
-70eb9f8000-70eb9fc000 rw-p 00000000 00:05 10270805                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eb9f6000-70eb9f8000 rw-p 00000000 00:05 10271002                       [anon:dalvik-indirect ref table]
+70eb9f8000-70eb9fc000 rw-p 00000000 00:05 10270805                       [anon:dalvik-thread local mark stack]
 70eb9fc000-70eb9fd000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70eb9fd000-70eb9ff000 rw-p 00000000 00:05 10270999                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70eb9fd000-70eb9ff000 rw-p 00000000 00:05 10270999                       [anon:dalvik-indirect ref table]
 70eb9ff000-70eba00000 r--s 00000000 fc:00 983                            /system/framework/com.google.vr.platform.jar
-70eba00000-70eba04000 rw-p 00000000 00:05 10270804                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba04000-70eba08000 rw-p 00000000 00:05 10270803                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba08000-70eba0c000 rw-p 00000000 00:05 10270802                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba0c000-70eba10000 rw-p 00000000 00:05 10270801                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba10000-70eba14000 rw-p 00000000 00:05 10270800                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba14000-70eba18000 rw-p 00000000 00:05 10270799                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba18000-70eba1c000 rw-p 00000000 00:05 10270798                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba1c000-70eba20000 rw-p 00000000 00:05 10270797                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba20000-70eba24000 rw-p 00000000 00:05 10270796                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba24000-70eba28000 rw-p 00000000 00:05 10270795                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba28000-70eba2c000 rw-p 00000000 00:05 10270794                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba00000-70eba04000 rw-p 00000000 00:05 10270804                       [anon:dalvik-thread local mark stack]
+70eba04000-70eba08000 rw-p 00000000 00:05 10270803                       [anon:dalvik-thread local mark stack]
+70eba08000-70eba0c000 rw-p 00000000 00:05 10270802                       [anon:dalvik-thread local mark stack]
+70eba0c000-70eba10000 rw-p 00000000 00:05 10270801                       [anon:dalvik-thread local mark stack]
+70eba10000-70eba14000 rw-p 00000000 00:05 10270800                       [anon:dalvik-thread local mark stack]
+70eba14000-70eba18000 rw-p 00000000 00:05 10270799                       [anon:dalvik-thread local mark stack]
+70eba18000-70eba1c000 rw-p 00000000 00:05 10270798                       [anon:dalvik-thread local mark stack]
+70eba1c000-70eba20000 rw-p 00000000 00:05 10270797                       [anon:dalvik-thread local mark stack]
+70eba20000-70eba24000 rw-p 00000000 00:05 10270796                       [anon:dalvik-thread local mark stack]
+70eba24000-70eba28000 rw-p 00000000 00:05 10270795                       [anon:dalvik-thread local mark stack]
+70eba28000-70eba2c000 rw-p 00000000 00:05 10270794                       [anon:dalvik-thread local mark stack]
 70eba2c000-70eba2d000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eba2d000-70eba2e000 r--s 00000000 fc:00 881                            /system/framework/android.test.base.jar
 70eba2e000-70eba2f000 r--s 00000000 fc:00 707                            /system/framework/framework-oahl-backward-compatibility.jar
 70eba2f000-70eba30000 r--s 00000000 fc:00 705                            /system/framework/android.hidl.manager-V1.0-java.jar
-70eba30000-70eba34000 rw-p 00000000 00:05 10270793                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba34000-70eba38000 rw-p 00000000 00:05 10270792                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba38000-70eba3c000 rw-p 00000000 00:05 10270791                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba3c000-70eba40000 rw-p 00000000 00:05 10270790                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba40000-70eba44000 rw-p 00000000 00:05 10270789                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba44000-70eba48000 rw-p 00000000 00:05 10270788                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba48000-70eba4c000 rw-p 00000000 00:05 10270787                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba4c000-70eba50000 rw-p 00000000 00:05 10270786                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba30000-70eba34000 rw-p 00000000 00:05 10270793                       [anon:dalvik-thread local mark stack]
+70eba34000-70eba38000 rw-p 00000000 00:05 10270792                       [anon:dalvik-thread local mark stack]
+70eba38000-70eba3c000 rw-p 00000000 00:05 10270791                       [anon:dalvik-thread local mark stack]
+70eba3c000-70eba40000 rw-p 00000000 00:05 10270790                       [anon:dalvik-thread local mark stack]
+70eba40000-70eba44000 rw-p 00000000 00:05 10270789                       [anon:dalvik-thread local mark stack]
+70eba44000-70eba48000 rw-p 00000000 00:05 10270788                       [anon:dalvik-thread local mark stack]
+70eba48000-70eba4c000 rw-p 00000000 00:05 10270787                       [anon:dalvik-thread local mark stack]
+70eba4c000-70eba50000 rw-p 00000000 00:05 10270786                       [anon:dalvik-thread local mark stack]
 70eba50000-70eba52000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70eba52000-70eba53000 r--s 00000000 fc:00 971                            /system/framework/android.hidl.base-V1.0-java.jar
-70eba53000-70eba57000 rw-p 00000000 00:05 10270785                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba57000-70eba5b000 rw-p 00000000 00:05 10270784                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba5b000-70eba5f000 rw-p 00000000 00:05 10270783                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba5f000-70eba63000 rw-p 00000000 00:05 10270782                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70eba53000-70eba57000 rw-p 00000000 00:05 10270785                       [anon:dalvik-thread local mark stack]
+70eba57000-70eba5b000 rw-p 00000000 00:05 10270784                       [anon:dalvik-thread local mark stack]
+70eba5b000-70eba5f000 rw-p 00000000 00:05 10270783                       [anon:dalvik-thread local mark stack]
+70eba5f000-70eba63000 rw-p 00000000 00:05 10270782                       [anon:dalvik-thread local mark stack]
 70eba63000-70eba64000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70eba64000-70eba65000 r--s 00000000 fc:00 889                            /system/framework/ims-common.jar
-70eba65000-70eba69000 rw-p 00000000 00:05 10270781                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba69000-70eba6d000 rw-p 00000000 00:05 10270780                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba6d000-70eba71000 rw-p 00000000 00:05 10270779                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba71000-70eba75000 rw-p 00000000 00:05 10270778                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70eba75000-70eba95000 rw-p 00000000 00:05 10267647                       /dev/ashmem/dalvik-large marked objects (deleted)
-70eba95000-70ebab5000 rw-p 00000000 00:05 10267646                       /dev/ashmem/dalvik-large live objects (deleted)
+70eba65000-70eba69000 rw-p 00000000 00:05 10270781                       [anon:dalvik-thread local mark stack]
+70eba69000-70eba6d000 rw-p 00000000 00:05 10270780                       [anon:dalvik-thread local mark stack]
+70eba6d000-70eba71000 rw-p 00000000 00:05 10270779                       [anon:dalvik-thread local mark stack]
+70eba71000-70eba75000 rw-p 00000000 00:05 10270778                       [anon:dalvik-thread local mark stack]
+70eba75000-70eba95000 rw-p 00000000 00:05 10267647                       [anon:dalvik-large marked objects]
+70eba95000-70ebab5000 rw-p 00000000 00:05 10267646                       [anon:dalvik-large live objects]
 70ebab5000-70ebab6000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70ebab6000-70ebab7000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70ebab7000-70ebabb000 rw-p 00000000 00:05 10270777                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebab7000-70ebabb000 rw-p 00000000 00:05 10270777                       [anon:dalvik-thread local mark stack]
 70ebabb000-70ebadb000 r--s 00000000 00:10 16603                          /dev/__properties__/u:object_r:exported_fingerprint_prop:s0
 70ebadb000-70ebadc000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70ebadc000-70ebadd000 r--s 00000000 fc:00 878                            /system/framework/voip-common.jar
-70ebadd000-70ebadf000 rw-p 00000000 00:05 10270995                       /dev/ashmem/dalvik-indirect ref table (deleted)
-70ebadf000-70ebae3000 rw-p 00000000 00:05 10270776                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebae3000-70ebae7000 rw-p 00000000 00:05 10270775                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebae7000-70ebaeb000 rw-p 00000000 00:05 10270774                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebaeb000-70ebaef000 rw-p 00000000 00:05 10270773                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebadd000-70ebadf000 rw-p 00000000 00:05 10270995                       [anon:dalvik-indirect ref table]
+70ebadf000-70ebae3000 rw-p 00000000 00:05 10270776                       [anon:dalvik-thread local mark stack]
+70ebae3000-70ebae7000 rw-p 00000000 00:05 10270775                       [anon:dalvik-thread local mark stack]
+70ebae7000-70ebaeb000 rw-p 00000000 00:05 10270774                       [anon:dalvik-thread local mark stack]
+70ebaeb000-70ebaef000 rw-p 00000000 00:05 10270773                       [anon:dalvik-thread local mark stack]
 70ebaef000-70ebb0f000 r--s 00000000 00:10 16582                          /dev/__properties__/u:object_r:debug_prop:s0
 70ebb0f000-70ebb10000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70ebb10000-70ebb11000 r--s 00000000 fc:00 703                            /system/framework/telephony-common.jar
-70ebb11000-70ebb13000 rw-p 00000000 00:05 10270994                       /dev/ashmem/dalvik-indirect ref table (deleted)
-70ebb13000-70ebb17000 rw-p 00000000 00:05 10270772                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebb11000-70ebb13000 rw-p 00000000 00:05 10270994                       [anon:dalvik-indirect ref table]
+70ebb13000-70ebb17000 rw-p 00000000 00:05 10270772                       [anon:dalvik-thread local mark stack]
 70ebb17000-70ebb19000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70ebb19000-70ebb1d000 rw-p 00000000 00:05 10270771                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebb19000-70ebb1d000 rw-p 00000000 00:05 10270771                       [anon:dalvik-thread local mark stack]
 70ebb1d000-70ebb3d000 r--s 00000000 00:10 16600                          /dev/__properties__/u:object_r:exported_default_prop:s0
 70ebb3d000-70ebb5d000 r--s 00000000 00:10 16650                          /dev/__properties__/u:object_r:system_prop:s0
 70ebb5d000-70ebb7d000 r--s 00000000 00:10 16610                          /dev/__properties__/u:object_r:exported_vold_prop:s0
 70ebb7d000-70ebb9d000 r--s 00000000 00:10 16598                          /dev/__properties__/u:object_r:exported_config_prop:s0
 70ebb9d000-70ebb9e000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70ebb9e000-70ebba2000 rw-p 00000000 00:05 10270770                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebba2000-70ebba6000 rw-p 00000000 00:05 10270769                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebb9e000-70ebba2000 rw-p 00000000 00:05 10270770                       [anon:dalvik-thread local mark stack]
+70ebba2000-70ebba6000 rw-p 00000000 00:05 10270769                       [anon:dalvik-thread local mark stack]
 70ebba6000-70ebba7000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70ebba7000-70ebba8000 r--s 00000000 fc:00 1004                           /system/framework/framework.jar
-70ebba8000-70ebbac000 rw-p 00000000 00:05 10270768                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbac000-70ebbb0000 rw-p 00000000 00:05 10270767                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbb0000-70ebbb4000 rw-p 00000000 00:05 10270766                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbb4000-70ebbb8000 rw-p 00000000 00:05 10270765                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbb8000-70ebbbc000 rw-p 00000000 00:05 10270764                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbbc000-70ebbc0000 rw-p 00000000 00:05 10270763                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbc0000-70ebbc4000 rw-p 00000000 00:05 10270762                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebba8000-70ebbac000 rw-p 00000000 00:05 10270768                       [anon:dalvik-thread local mark stack]
+70ebbac000-70ebbb0000 rw-p 00000000 00:05 10270767                       [anon:dalvik-thread local mark stack]
+70ebbb0000-70ebbb4000 rw-p 00000000 00:05 10270766                       [anon:dalvik-thread local mark stack]
+70ebbb4000-70ebbb8000 rw-p 00000000 00:05 10270765                       [anon:dalvik-thread local mark stack]
+70ebbb8000-70ebbbc000 rw-p 00000000 00:05 10270764                       [anon:dalvik-thread local mark stack]
+70ebbbc000-70ebbc0000 rw-p 00000000 00:05 10270763                       [anon:dalvik-thread local mark stack]
+70ebbc0000-70ebbc4000 rw-p 00000000 00:05 10270762                       [anon:dalvik-thread local mark stack]
 70ebbc4000-70ebbe4000 r--s 00000000 00:10 16581                          /dev/__properties__/u:object_r:dalvik_prop:s0
 70ebbe4000-70ebbe5000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70ebbe5000-70ebbe6000 r--s 00004000 fc:00 877                            /system/framework/apache-xml.jar
-70ebbe6000-70ebbe8000 rw-p 00000000 00:05 10270993                       /dev/ashmem/dalvik-indirect ref table (deleted)
-70ebbe8000-70ebbec000 rw-p 00000000 00:05 10270761                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbec000-70ebbf0000 rw-p 00000000 00:05 10270760                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbf0000-70ebbf4000 rw-p 00000000 00:05 10270759                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebbf4000-70ebbf8000 rw-p 00000000 00:05 10270758                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbe6000-70ebbe8000 rw-p 00000000 00:05 10270993                       [anon:dalvik-indirect ref table]
+70ebbe8000-70ebbec000 rw-p 00000000 00:05 10270761                       [anon:dalvik-thread local mark stack]
+70ebbec000-70ebbf0000 rw-p 00000000 00:05 10270760                       [anon:dalvik-thread local mark stack]
+70ebbf0000-70ebbf4000 rw-p 00000000 00:05 10270759                       [anon:dalvik-thread local mark stack]
+70ebbf4000-70ebbf8000 rw-p 00000000 00:05 10270758                       [anon:dalvik-thread local mark stack]
 70ebbf8000-70ebbf9000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70ebbf9000-70ebbfa000 r--s 00000000 fc:00 968                            /system/framework/bouncycastle.jar
-70ebbfa000-70ebbfc000 rw-p 00000000 00:05 10270992                       /dev/ashmem/dalvik-indirect ref table (deleted)
-70ebbfc000-70ebc00000 rw-p 00000000 00:05 10270757                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc00000-70ebc04000 rw-p 00000000 00:05 10270756                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc04000-70ebc08000 rw-p 00000000 00:05 10270755                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc08000-70ebc0c000 rw-p 00000000 00:05 10270754                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc0c000-70ebc10000 rw-p 00000000 00:05 10270753                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc10000-70ebc14000 rw-p 00000000 00:05 10270752                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebbfa000-70ebbfc000 rw-p 00000000 00:05 10270992                       [anon:dalvik-indirect ref table]
+70ebbfc000-70ebc00000 rw-p 00000000 00:05 10270757                       [anon:dalvik-thread local mark stack]
+70ebc00000-70ebc04000 rw-p 00000000 00:05 10270756                       [anon:dalvik-thread local mark stack]
+70ebc04000-70ebc08000 rw-p 00000000 00:05 10270755                       [anon:dalvik-thread local mark stack]
+70ebc08000-70ebc0c000 rw-p 00000000 00:05 10270754                       [anon:dalvik-thread local mark stack]
+70ebc0c000-70ebc10000 rw-p 00000000 00:05 10270753                       [anon:dalvik-thread local mark stack]
+70ebc10000-70ebc14000 rw-p 00000000 00:05 10270752                       [anon:dalvik-thread local mark stack]
 70ebc14000-70ebc15000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70ebc15000-70ebc16000 r--s 00000000 fc:00 960                            /system/framework/okhttp.jar
-70ebc16000-70ebc1a000 rw-p 00000000 00:05 10270751                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc1a000-70ebc1e000 rw-p 00000000 00:05 10270750                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc16000-70ebc1a000 rw-p 00000000 00:05 10270751                       [anon:dalvik-thread local mark stack]
+70ebc1a000-70ebc1e000 rw-p 00000000 00:05 10270750                       [anon:dalvik-thread local mark stack]
 70ebc1e000-70ebc3e000 r--s 00000000 00:10 16584                          /dev/__properties__/u:object_r:default_prop:s0
 70ebc3e000-70ebc3f000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70ebc3f000-70ebc40000 r--s 00000000 fc:00 974                            /system/framework/conscrypt.jar
-70ebc40000-70ebc42000 rw-p 00000000 00:05 10269719                       /dev/ashmem/dalvik-indirect ref table (deleted)
-70ebc42000-70ebc46000 rw-p 00000000 00:05 10270749                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc46000-70ebc4a000 rw-p 00000000 00:05 10270748                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc4a000-70ebc4e000 rw-p 00000000 00:05 10270747                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc40000-70ebc42000 rw-p 00000000 00:05 10269719                       [anon:dalvik-indirect ref table]
+70ebc42000-70ebc46000 rw-p 00000000 00:05 10270749                       [anon:dalvik-thread local mark stack]
+70ebc46000-70ebc4a000 rw-p 00000000 00:05 10270748                       [anon:dalvik-thread local mark stack]
+70ebc4a000-70ebc4e000 rw-p 00000000 00:05 10270747                       [anon:dalvik-thread local mark stack]
 70ebc4e000-70ebc4f000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70ebc4f000-70ebc51000 rw-p 00000000 00:05 10269718                       /dev/ashmem/dalvik-indirect ref table (deleted)
-70ebc51000-70ebc55000 rw-p 00000000 00:05 10270746                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc55000-70ebc59000 rw-p 00000000 00:05 10270745                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc59000-70ebc5d000 rw-p 00000000 00:05 10270744                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc4f000-70ebc51000 rw-p 00000000 00:05 10269718                       [anon:dalvik-indirect ref table]
+70ebc51000-70ebc55000 rw-p 00000000 00:05 10270746                       [anon:dalvik-thread local mark stack]
+70ebc55000-70ebc59000 rw-p 00000000 00:05 10270745                       [anon:dalvik-thread local mark stack]
+70ebc59000-70ebc5d000 rw-p 00000000 00:05 10270744                       [anon:dalvik-thread local mark stack]
 70ebc5d000-70ebc7d000 r--s 00000000 00:10 16599                          /dev/__properties__/u:object_r:exported_dalvik_prop:s0
 70ebc7d000-70ebc7e000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70ebc7e000-70ebc7f000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70ebc7f000-70ebc80000 r--s 00004000 fc:00 963                            /system/framework/core-libart.jar
 70ebc80000-70ebc81000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70ebc81000-70ebc85000 rw-p 00000000 00:05 10270743                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc85000-70ebc89000 rw-p 00000000 00:05 10270742                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc81000-70ebc85000 rw-p 00000000 00:05 10270743                       [anon:dalvik-thread local mark stack]
+70ebc85000-70ebc89000 rw-p 00000000 00:05 10270742                       [anon:dalvik-thread local mark stack]
 70ebc89000-70ebc8a000 rw-p 00000000 00:00 0                              [anon:linker_alloc_small_objects]
 70ebc8a000-70ebc8c000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70ebc8c000-70ebc8d000 r--s 0001e000 fc:00 699                            /system/framework/core-oj.jar
-70ebc8d000-70ebc91000 rw-p 00000000 00:05 10270741                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc91000-70ebc95000 rw-p 00000000 00:05 10270740                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebc95000-70ebc99000 rw-p 00000000 00:05 10270739                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc8d000-70ebc91000 rw-p 00000000 00:05 10270741                       [anon:dalvik-thread local mark stack]
+70ebc91000-70ebc95000 rw-p 00000000 00:05 10270740                       [anon:dalvik-thread local mark stack]
+70ebc95000-70ebc99000 rw-p 00000000 00:05 10270739                       [anon:dalvik-thread local mark stack]
 70ebc99000-70ebc9b000 r--p 00000000 00:00 0                              [anon:linker_alloc]
 70ebc9b000-70ebc9c000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70ebc9c000-70ebca0000 rw-p 00000000 00:05 10270738                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebca0000-70ebca4000 rw-p 00000000 00:05 10270737                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebca4000-70ebca8000 rw-p 00000000 00:05 10270736                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebca8000-70ebcac000 rw-p 00000000 00:05 10270735                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebcac000-70ebcb0000 rw-p 00000000 00:05 10270734                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebc9c000-70ebca0000 rw-p 00000000 00:05 10270738                       [anon:dalvik-thread local mark stack]
+70ebca0000-70ebca4000 rw-p 00000000 00:05 10270737                       [anon:dalvik-thread local mark stack]
+70ebca4000-70ebca8000 rw-p 00000000 00:05 10270736                       [anon:dalvik-thread local mark stack]
+70ebca8000-70ebcac000 rw-p 00000000 00:05 10270735                       [anon:dalvik-thread local mark stack]
+70ebcac000-70ebcb0000 rw-p 00000000 00:05 10270734                       [anon:dalvik-thread local mark stack]
 70ebcb0000-70ebcd0000 r--s 00000000 00:10 16592                          /dev/__properties__/u:object_r:exported2_system_prop:s0
 70ebcd0000-70ebcd1000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
-70ebcd1000-70ebcd5000 rw-p 00000000 00:05 10270733                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebcd5000-70ebcd9000 rw-p 00000000 00:05 10270732                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebcd9000-70ebcdd000 rw-p 00000000 00:05 10270731                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebcdd000-70ebce1000 rw-p 00000000 00:05 10270730                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebce1000-70ebce5000 rw-p 00000000 00:05 10270729                       /dev/ashmem/dalvik-thread local mark stack (deleted)
-70ebce5000-70ebce9000 rw-p 00000000 00:05 10270728                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebcd1000-70ebcd5000 rw-p 00000000 00:05 10270733                       [anon:dalvik-thread local mark stack]
+70ebcd5000-70ebcd9000 rw-p 00000000 00:05 10270732                       [anon:dalvik-thread local mark stack]
+70ebcd9000-70ebcdd000 rw-p 00000000 00:05 10270731                       [anon:dalvik-thread local mark stack]
+70ebcdd000-70ebce1000 rw-p 00000000 00:05 10270730                       [anon:dalvik-thread local mark stack]
+70ebce1000-70ebce5000 rw-p 00000000 00:05 10270729                       [anon:dalvik-thread local mark stack]
+70ebce5000-70ebce9000 rw-p 00000000 00:05 10270728                       [anon:dalvik-thread local mark stack]
 70ebce9000-70ebcea000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70ebcea000-70ebcec000 rw-p 00000000 00:05 10270987                       /dev/ashmem/dalvik-indirect ref table (deleted)
+70ebcea000-70ebcec000 rw-p 00000000 00:05 10270987                       [anon:dalvik-indirect ref table]
 70ebcec000-70ebcf9000 r--p 00646000 103:1d 639532                        /data/dalvik-cache/arm64/system@framework@boot-framework.art
 70ebcf9000-70ebd19000 r--s 00000000 00:10 16620                          /dev/__properties__/u:object_r:log_tag_prop:s0
 70ebd19000-70ebd39000 r--s 00000000 00:10 16621                          /dev/__properties__/u:object_r:logd_prop:s0
 70ebd39000-70ebd3a000 rw-p 00000000 00:00 0                              [anon:linker_alloc]
 70ebd3a000-70ebd3b000 r--p 00000000 00:00 0                              [anon:linker_alloc]
-70ebd3b000-70ebd3f000 rw-p 00000000 00:05 10270727                       /dev/ashmem/dalvik-thread local mark stack (deleted)
+70ebd3b000-70ebd3f000 rw-p 00000000 00:05 10270727                       [anon:dalvik-thread local mark stack]
 70ebd3f000-70ebd40000 r--p 00002000 103:1d 639556                        /data/dalvik-cache/arm64/system@framework@boot-com.google.vr.platform.art
 70ebd40000-70ebd41000 r--p 00005000 103:1d 639553                        /data/dalvik-cache/arm64/system@framework@boot-android.test.base.art
 70ebd41000-70ebd42000 r--p 00000000 00:00 0                              [anon:linker_alloc]
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 3a12292..29d23c8 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -27,3 +27,16 @@
 
     export_include_dirs: ["include"],
 }
+
+cc_test {
+    name: "libsysutils_tests",
+    test_suites: ["device-tests"],
+    srcs: [
+        "src/SocketListener_test.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libsysutils",
+    ],
+}
diff --git a/libsysutils/include/sysutils/FrameworkCommand.h b/libsysutils/include/sysutils/FrameworkCommand.h
index 3e6264b..db17ba7 100644
--- a/libsysutils/include/sysutils/FrameworkCommand.h
+++ b/libsysutils/include/sysutils/FrameworkCommand.h
@@ -16,8 +16,6 @@
 #ifndef __FRAMEWORK_CMD_HANDLER_H
 #define __FRAMEWORK_CMD_HANDLER_H
 
-#include "List.h"
-
 class SocketClient;
 
 class FrameworkCommand { 
@@ -31,8 +29,7 @@
 
     virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
 
-    const char *getCommand() { return mCommand; }
+    const char* getCommand() const { return mCommand; }
 };
 
-typedef android::sysutils::List<FrameworkCommand *> FrameworkCommandCollection;
 #endif
diff --git a/libsysutils/include/sysutils/FrameworkListener.h b/libsysutils/include/sysutils/FrameworkListener.h
index 2137069..93d99c4 100644
--- a/libsysutils/include/sysutils/FrameworkListener.h
+++ b/libsysutils/include/sysutils/FrameworkListener.h
@@ -17,8 +17,10 @@
 #define _FRAMEWORKSOCKETLISTENER_H
 
 #include "SocketListener.h"
-#include "FrameworkCommand.h"
 
+#include <vector>
+
+class FrameworkCommand;
 class SocketClient;
 
 class FrameworkListener : public SocketListener {
@@ -31,20 +33,20 @@
 private:
     int mCommandCount;
     bool mWithSeq;
-    FrameworkCommandCollection *mCommands;
+    std::vector<FrameworkCommand*> mCommands;
     bool mSkipToNextNullByte;
 
 public:
     FrameworkListener(const char *socketName);
     FrameworkListener(const char *socketName, bool withSeq);
     FrameworkListener(int sock);
-    virtual ~FrameworkListener() {}
+    ~FrameworkListener() override {}
 
-protected:
+  protected:
     void registerCmd(FrameworkCommand *cmd);
-    virtual bool onDataAvailable(SocketClient *c);
+    bool onDataAvailable(SocketClient* c) override;
 
-private:
+  private:
     void dispatchCommand(SocketClient *c, char *data);
     void init(const char *socketName, bool withSeq);
 };
diff --git a/libsysutils/include/sysutils/List.h b/libsysutils/include/sysutils/List.h
deleted file mode 100644
index 31f7b37..0000000
--- a/libsysutils/include/sysutils/List.h
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Templated list class.  Normally we'd use STL, but we don't have that.
-// This class mimics STL's interfaces.
-//
-// Objects are copied into the list with the '=' operator or with copy-
-// construction, so if the compiler's auto-generated versions won't work for
-// you, define your own.
-//
-// The only class you want to use from here is "List".
-//
-#ifndef _SYSUTILS_LIST_H
-#define _SYSUTILS_LIST_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-namespace android {
-namespace sysutils {
-
-/*
- * Doubly-linked list.  Instantiate with "List<MyClass> myList".
- *
- * Objects added to the list are copied using the assignment operator,
- * so this must be defined.
- */
-template<typename T> 
-class List 
-{
-protected:
-    /*
-     * One element in the list.
-     */
-    class _Node {
-    public:
-        explicit _Node(const T& val) : mVal(val) {}
-        ~_Node() {}
-        inline T& getRef() { return mVal; }
-        inline const T& getRef() const { return mVal; }
-        inline _Node* getPrev() const { return mpPrev; }
-        inline _Node* getNext() const { return mpNext; }
-        inline void setVal(const T& val) { mVal = val; }
-        inline void setPrev(_Node* ptr) { mpPrev = ptr; }
-        inline void setNext(_Node* ptr) { mpNext = ptr; }
-    private:
-        friend class List;
-        friend class _ListIterator;
-        T           mVal;
-        _Node*      mpPrev;
-        _Node*      mpNext;
-    };
-
-    /*
-     * Iterator for walking through the list.
-     */
-    
-    template <typename TYPE>
-    struct CONST_ITERATOR {
-        typedef _Node const * NodePtr;
-        typedef const TYPE Type;
-    };
-    
-    template <typename TYPE>
-    struct NON_CONST_ITERATOR {
-        typedef _Node* NodePtr;
-        typedef TYPE Type;
-    };
-    
-    template<
-        typename U,
-        template <class> class Constness
-    > 
-    class _ListIterator {
-        typedef _ListIterator<U, Constness>     _Iter;
-        typedef typename Constness<U>::NodePtr  _NodePtr;
-        typedef typename Constness<U>::Type     _Type;
-
-        explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
-
-    public:
-        _ListIterator() {}
-        _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
-        ~_ListIterator() {}
-        
-        // this will handle conversions from iterator to const_iterator
-        // (and also all convertible iterators)
-        // Here, in this implementation, the iterators can be converted
-        // if the nodes can be converted
-        template<typename V> explicit 
-        _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
-        
-
-        /*
-         * Dereference operator.  Used to get at the juicy insides.
-         */
-        _Type& operator*() const { return mpNode->getRef(); }
-        _Type* operator->() const { return &(mpNode->getRef()); }
-
-        /*
-         * Iterator comparison.
-         */
-        inline bool operator==(const _Iter& right) const { 
-            return mpNode == right.mpNode; }
-        
-        inline bool operator!=(const _Iter& right) const { 
-            return mpNode != right.mpNode; }
-
-        /*
-         * handle comparisons between iterator and const_iterator
-         */
-        template<typename OTHER>
-        inline bool operator==(const OTHER& right) const { 
-            return mpNode == right.mpNode; }
-        
-        template<typename OTHER>
-        inline bool operator!=(const OTHER& right) const { 
-            return mpNode != right.mpNode; }
-
-        /*
-         * Incr/decr, used to move through the list.
-         */
-        inline _Iter& operator++() {     // pre-increment
-            mpNode = mpNode->getNext();
-            return *this;
-        }
-        const _Iter operator++(int) {    // post-increment
-            _Iter tmp(*this);
-            mpNode = mpNode->getNext();
-            return tmp;
-        }
-        inline _Iter& operator--() {     // pre-increment
-            mpNode = mpNode->getPrev();
-            return *this;
-        }
-        const _Iter operator--(int) {   // post-increment
-            _Iter tmp(*this);
-            mpNode = mpNode->getPrev();
-            return tmp;
-        }
-
-        inline _NodePtr getNode() const { return mpNode; }
-
-        _NodePtr mpNode;    /* should be private, but older gcc fails */
-    private:
-        friend class List;
-    };
-
-public:
-    List() {
-        prep();
-    }
-    List(const List<T>& src) {      // copy-constructor
-        prep();
-        insert(begin(), src.begin(), src.end());
-    }
-    virtual ~List() {
-        clear();
-        delete[] (unsigned char*) mpMiddle;
-    }
-
-    typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
-    typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
-
-    List<T>& operator=(const List<T>& right);
-
-    /* returns true if the list is empty */
-    inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
-
-    /* return #of elements in list */
-    size_t size() const {
-        return size_t(distance(begin(), end()));
-    }
-
-    /*
-     * Return the first element or one past the last element.  The
-     * _Node* we're returning is converted to an "iterator" by a
-     * constructor in _ListIterator.
-     */
-    inline iterator begin() { 
-        return iterator(mpMiddle->getNext()); 
-    }
-    inline const_iterator begin() const { 
-        return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); 
-    }
-    inline iterator end() { 
-        return iterator(mpMiddle); 
-    }
-    inline const_iterator end() const { 
-        return const_iterator(const_cast<_Node const*>(mpMiddle)); 
-    }
-
-    /* add the object to the head or tail of the list */
-    void push_front(const T& val) { insert(begin(), val); }
-    void push_back(const T& val) { insert(end(), val); }
-
-    /* insert before the current node; returns iterator at new node */
-    iterator insert(iterator posn, const T& val) 
-    {
-        _Node* newNode = new _Node(val);        // alloc & copy-construct
-        newNode->setNext(posn.getNode());
-        newNode->setPrev(posn.getNode()->getPrev());
-        posn.getNode()->getPrev()->setNext(newNode);
-        posn.getNode()->setPrev(newNode);
-        return iterator(newNode);
-    }
-
-    /* insert a range of elements before the current node */
-    void insert(iterator posn, const_iterator first, const_iterator last) {
-        for ( ; first != last; ++first)
-            insert(posn, *first);
-    }
-
-    /* remove one entry; returns iterator at next node */
-    iterator erase(iterator posn) {
-        _Node* pNext = posn.getNode()->getNext();
-        _Node* pPrev = posn.getNode()->getPrev();
-        pPrev->setNext(pNext);
-        pNext->setPrev(pPrev);
-        delete posn.getNode();
-        return iterator(pNext);
-    }
-
-    /* remove a range of elements */
-    iterator erase(iterator first, iterator last) {
-        while (first != last)
-            erase(first++);     // don't erase than incr later!
-        return iterator(last);
-    }
-
-    /* remove all contents of the list */
-    void clear() {
-        _Node* pCurrent = mpMiddle->getNext();
-        _Node* pNext;
-
-        while (pCurrent != mpMiddle) {
-            pNext = pCurrent->getNext();
-            delete pCurrent;
-            pCurrent = pNext;
-        }
-        mpMiddle->setPrev(mpMiddle);
-        mpMiddle->setNext(mpMiddle);
-    }
-
-    /*
-     * Measure the distance between two iterators.  On exist, "first"
-     * will be equal to "last".  The iterators must refer to the same
-     * list.
-     *
-     * FIXME: This is actually a generic iterator function. It should be a 
-     * template function at the top-level with specializations for things like
-     * vector<>, which can just do pointer math). Here we limit it to
-     * _ListIterator of the same type but different constness.
-     */
-    template<
-        typename U,
-        template <class> class CL,
-        template <class> class CR
-    > 
-    ptrdiff_t distance(
-            _ListIterator<U, CL> first, _ListIterator<U, CR> last) const 
-    {
-        ptrdiff_t count = 0;
-        while (first != last) {
-            ++first;
-            ++count;
-        }
-        return count;
-    }
-
-private:
-    /*
-     * I want a _Node but don't need it to hold valid data.  More
-     * to the point, I don't want T's constructor to fire, since it
-     * might have side-effects or require arguments.  So, we do this
-     * slightly uncouth storage alloc.
-     */
-    void prep() {
-        mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
-        mpMiddle->setPrev(mpMiddle);
-        mpMiddle->setNext(mpMiddle);
-    }
-
-    /*
-     * This node plays the role of "pointer to head" and "pointer to tail".
-     * It sits in the middle of a circular list of nodes.  The iterator
-     * runs around the circle until it encounters this one.
-     */
-    _Node*      mpMiddle;
-};
-
-/*
- * Assignment operator.
- *
- * The simplest way to do this would be to clear out the target list and
- * fill it with the source.  However, we can speed things along by
- * re-using existing elements.
- */
-template<class T>
-List<T>& List<T>::operator=(const List<T>& right)
-{
-    if (this == &right)
-        return *this;       // self-assignment
-    iterator firstDst = begin();
-    iterator lastDst = end();
-    const_iterator firstSrc = right.begin();
-    const_iterator lastSrc = right.end();
-    while (firstSrc != lastSrc && firstDst != lastDst)
-        *firstDst++ = *firstSrc++;
-    if (firstSrc == lastSrc)        // ran out of elements in source?
-        erase(firstDst, lastDst);   // yes, erase any extras
-    else
-        insert(lastDst, firstSrc, lastSrc);     // copy remaining over
-    return *this;
-}
-
-}; // namespace sysutils
-}; // namespace android
-
-#endif // _SYSUTILS_LIST_H
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
index b78918e..645baf4 100644
--- a/libsysutils/include/sysutils/OWNERS
+++ b/libsysutils/include/sysutils/OWNERS
@@ -1,2 +1 @@
-per-file Netlink* = ek@google.com
-per-file Netlink* = lorenzo@google.com
+per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
diff --git a/libsysutils/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h
index 1004f06..c657526 100644
--- a/libsysutils/include/sysutils/SocketClient.h
+++ b/libsysutils/include/sysutils/SocketClient.h
@@ -1,8 +1,6 @@
 #ifndef _SOCKET_CLIENT_H
 #define _SOCKET_CLIENT_H
 
-#include "List.h"
-
 #include <pthread.h>
 #include <cutils/atomic.h>
 #include <sys/types.h>
@@ -35,7 +33,7 @@
     SocketClient(int sock, bool owned, bool useCmdNum);
     virtual ~SocketClient();
 
-    int getSocket() { return mSocket; }
+    int getSocket() const { return mSocket; }
     pid_t getPid() const { return mPid; }
     uid_t getUid() const { return mUid; }
     gid_t getGid() const { return mGid; }
@@ -84,5 +82,4 @@
     int sendDataLockedv(struct iovec *iov, int iovcnt);
 };
 
-typedef android::sysutils::List<SocketClient *> SocketClientCollection;
 #endif
diff --git a/libsysutils/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
index bc93b86..67a691a 100644
--- a/libsysutils/include/sysutils/SocketListener.h
+++ b/libsysutils/include/sysutils/SocketListener.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@
 
 #include <pthread.h>
 
+#include <unordered_map>
+
 #include <sysutils/SocketClient.h>
 #include "SocketClientCommand.h"
 
@@ -25,7 +27,7 @@
     bool                    mListen;
     const char              *mSocketName;
     int                     mSock;
-    SocketClientCollection  *mClients;
+    std::unordered_map<int, SocketClient*> mClients;
     pthread_mutex_t         mClientsLock;
     int                     mCtrlPipe[2];
     pthread_t               mThread;
@@ -51,8 +53,13 @@
     virtual bool onDataAvailable(SocketClient *c) = 0;
 
 private:
-    bool release(SocketClient *c, bool wakeup);
     static void *threadStart(void *obj);
+
+    // Add all clients to a separate list, so we don't have to hold the lock
+    // while processing it.
+    std::vector<SocketClient*> snapshotClients();
+
+    bool release(SocketClient *c, bool wakeup);
     void runListener();
     void init(const char *socketName, int socketFd, bool listen, bool useCmdNum);
 };
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 835e226..b07853a 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -28,8 +28,6 @@
 
 static const int CMD_BUF_SIZE = 1024;
 
-#define UNUSED __attribute__((unused))
-
 FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
                             SocketListener(socketName, true, withSeq) {
     init(socketName, withSeq);
@@ -45,8 +43,7 @@
     init(nullptr, false);
 }
 
-void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
-    mCommands = new FrameworkCommandCollection();
+void FrameworkListener::init(const char* /*socketName*/, bool withSeq) {
     errorRate = 0;
     mCommandCount = 0;
     mWithSeq = withSeq;
@@ -91,11 +88,10 @@
 }
 
 void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
-    mCommands->push_back(cmd);
+    mCommands.push_back(cmd);
 }
 
 void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
-    FrameworkCommandCollection::iterator i;
     int argc = 0;
     char *argv[FrameworkListener::CMD_ARGS_MAX];
     char tmp[CMD_BUF_SIZE];
@@ -193,9 +189,7 @@
         goto out;
     }
 
-    for (i = mCommands->begin(); i != mCommands->end(); ++i) {
-        FrameworkCommand *c = *i;
-
+    for (auto* c : mCommands) {
         if (!strcmp(argv[0], c->getCommand())) {
             if (c->runCommand(cli, argc, argv)) {
                 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
index b78918e..645baf4 100644
--- a/libsysutils/src/OWNERS
+++ b/libsysutils/src/OWNERS
@@ -1,2 +1 @@
-per-file Netlink* = ek@google.com
-per-file Netlink* = lorenzo@google.com
+per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index 0625db7..fe2f3d6 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -27,6 +27,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+#include <android-base/macros.h>
 #include <log/log.h>
 #include <sysutils/SocketClient.h>
 
@@ -145,7 +147,8 @@
         switch (*arg) {
         case '\\':
         case '"':
-            *(current++) = '\\'; // fallthrough
+            *(current++) = '\\';
+            FALLTHROUGH_INTENDED;
         default:
             *(current++) = *(arg++);
         }
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 0c8a688..ded5adb 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,13 +19,15 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/select.h>
+#include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <vector>
+
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <sysutils/SocketListener.h>
@@ -52,7 +54,6 @@
     mSock = socketFd;
     mUseCmdNum = useCmdNum;
     pthread_mutex_init(&mClientsLock, nullptr);
-    mClients = new SocketClientCollection();
 }
 
 SocketListener::~SocketListener() {
@@ -63,12 +64,9 @@
         close(mCtrlPipe[0]);
         close(mCtrlPipe[1]);
     }
-    SocketClientCollection::iterator it;
-    for (it = mClients->begin(); it != mClients->end();) {
-        (*it)->decRef();
-        it = mClients->erase(it);
+    for (auto pair : mClients) {
+        pair.second->decRef();
     }
-    delete mClients;
 }
 
 int SocketListener::startListener() {
@@ -95,7 +93,7 @@
         SLOGE("Unable to listen on socket (%s)", strerror(errno));
         return -1;
     } else if (!mListen)
-        mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
+        mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
 
     if (pipe(mCtrlPipe)) {
         SLOGE("pipe failed (%s)", strerror(errno));
@@ -135,11 +133,10 @@
         mSock = -1;
     }
 
-    SocketClientCollection::iterator it;
-    for (it = mClients->begin(); it != mClients->end();) {
-        delete (*it);
-        it = mClients->erase(it);
+    for (auto pair : mClients) {
+        delete pair.second;
     }
+    mClients.clear();
     return 0;
 }
 
@@ -152,47 +149,30 @@
 }
 
 void SocketListener::runListener() {
-
-    SocketClientCollection pendingList;
-
-    while(1) {
-        SocketClientCollection::iterator it;
-        fd_set read_fds;
-        int rc = 0;
-        int max = -1;
-
-        FD_ZERO(&read_fds);
-
-        if (mListen) {
-            max = mSock;
-            FD_SET(mSock, &read_fds);
-        }
-
-        FD_SET(mCtrlPipe[0], &read_fds);
-        if (mCtrlPipe[0] > max)
-            max = mCtrlPipe[0];
+    while (true) {
+        std::vector<pollfd> fds;
 
         pthread_mutex_lock(&mClientsLock);
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
+        fds.reserve(2 + mClients.size());
+        fds.push_back({.fd = mCtrlPipe[0], .events = POLLIN});
+        if (mListen) fds.push_back({.fd = mSock, .events = POLLIN});
+        for (auto pair : mClients) {
             // NB: calling out to an other object with mClientsLock held (safe)
-            int fd = (*it)->getSocket();
-            FD_SET(fd, &read_fds);
-            if (fd > max) {
-                max = fd;
-            }
+            const int fd = pair.second->getSocket();
+            if (fd != pair.first) SLOGE("fd mismatch: %d != %d", fd, pair.first);
+            fds.push_back({.fd = fd, .events = POLLIN});
         }
         pthread_mutex_unlock(&mClientsLock);
-        SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
-        if ((rc = select(max + 1, &read_fds, nullptr, nullptr, nullptr)) < 0) {
-            if (errno == EINTR)
-                continue;
-            SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
+
+        SLOGV("mListen=%d, mSocketName=%s", mListen, mSocketName);
+        int rc = TEMP_FAILURE_RETRY(poll(fds.data(), fds.size(), -1));
+        if (rc < 0) {
+            SLOGE("poll failed (%s) mListen=%d", strerror(errno), mListen);
             sleep(1);
             continue;
-        } else if (!rc)
-            continue;
+        }
 
-        if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
+        if (fds[0].revents & (POLLIN | POLLERR)) {
             char c = CtrlPipe_Shutdown;
             TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
             if (c == CtrlPipe_Shutdown) {
@@ -200,7 +180,7 @@
             }
             continue;
         }
-        if (mListen && FD_ISSET(mSock, &read_fds)) {
+        if (mListen && (fds[1].revents & (POLLIN | POLLERR))) {
             int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));
             if (c < 0) {
                 SLOGE("accept failed (%s)", strerror(errno));
@@ -208,32 +188,33 @@
                 continue;
             }
             pthread_mutex_lock(&mClientsLock);
-            mClients->push_back(new SocketClient(c, true, mUseCmdNum));
+            mClients[c] = new SocketClient(c, true, mUseCmdNum);
             pthread_mutex_unlock(&mClientsLock);
         }
 
-        /* Add all active clients to the pending list first */
-        pendingList.clear();
+        // Add all active clients to the pending list first, so we can release
+        // the lock before invoking the callbacks.
+        std::vector<SocketClient*> pending;
         pthread_mutex_lock(&mClientsLock);
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
-            SocketClient* c = *it;
-            // NB: calling out to an other object with mClientsLock held (safe)
-            int fd = c->getSocket();
-            if (FD_ISSET(fd, &read_fds)) {
-                pendingList.push_back(c);
+        const int size = fds.size();
+        for (int i = mListen ? 2 : 1; i < size; ++i) {
+            const struct pollfd& p = fds[i];
+            if (p.revents & (POLLIN | POLLERR)) {
+                auto it = mClients.find(p.fd);
+                if (it == mClients.end()) {
+                    SLOGE("fd vanished: %d", p.fd);
+                    continue;
+                }
+                SocketClient* c = it->second;
+                pending.push_back(c);
                 c->incRef();
             }
         }
         pthread_mutex_unlock(&mClientsLock);
 
-        /* Process the pending list, since it is owned by the thread,
-         * there is no need to lock it */
-        while (!pendingList.empty()) {
-            /* Pop the first item from the list */
-            it = pendingList.begin();
-            SocketClient* c = *it;
-            pendingList.erase(it);
-            /* Process it, if false is returned, remove from list */
+        for (SocketClient* c : pending) {
+            // Process it, if false is returned, remove from the map
+            SLOGV("processing fd %d", c->getSocket());
             if (!onDataAvailable(c)) {
                 release(c, false);
             }
@@ -246,17 +227,10 @@
     bool ret = false;
     /* if our sockets are connection-based, remove and destroy it */
     if (mListen && c) {
-        /* Remove the client from our array */
+        /* Remove the client from our map */
         SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
         pthread_mutex_lock(&mClientsLock);
-        SocketClientCollection::iterator it;
-        for (it = mClients->begin(); it != mClients->end(); ++it) {
-            if (*it == c) {
-                mClients->erase(it);
-                ret = true;
-                break;
-            }
-        }
+        ret = (mClients.erase(c->getSocket()) != 0);
         pthread_mutex_unlock(&mClientsLock);
         if (ret) {
             ret = c->decRef();
@@ -269,26 +243,22 @@
     return ret;
 }
 
-void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
-    SocketClientCollection safeList;
-
-    /* Add all active clients to the safe list first */
-    safeList.clear();
+std::vector<SocketClient*> SocketListener::snapshotClients() {
+    std::vector<SocketClient*> clients;
     pthread_mutex_lock(&mClientsLock);
-    SocketClientCollection::iterator i;
-
-    for (i = mClients->begin(); i != mClients->end(); ++i) {
-        SocketClient* c = *i;
+    clients.reserve(mClients.size());
+    for (auto pair : mClients) {
+        SocketClient* c = pair.second;
         c->incRef();
-        safeList.push_back(c);
+        clients.push_back(c);
     }
     pthread_mutex_unlock(&mClientsLock);
 
-    while (!safeList.empty()) {
-        /* Pop the first item from the list */
-        i = safeList.begin();
-        SocketClient* c = *i;
-        safeList.erase(i);
+    return clients;
+}
+
+void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
+    for (SocketClient* c : snapshotClients()) {
         // broadcasts are unsolicited and should not include a cmd number
         if (c->sendMsg(code, msg, addErrno, false)) {
             SLOGW("Error sending broadcast (%s)", strerror(errno));
@@ -298,25 +268,7 @@
 }
 
 void SocketListener::runOnEachSocket(SocketClientCommand *command) {
-    SocketClientCollection safeList;
-
-    /* Add all active clients to the safe list first */
-    safeList.clear();
-    pthread_mutex_lock(&mClientsLock);
-    SocketClientCollection::iterator i;
-
-    for (i = mClients->begin(); i != mClients->end(); ++i) {
-        SocketClient* c = *i;
-        c->incRef();
-        safeList.push_back(c);
-    }
-    pthread_mutex_unlock(&mClientsLock);
-
-    while (!safeList.empty()) {
-        /* Pop the first item from the list */
-        i = safeList.begin();
-        SocketClient* c = *i;
-        safeList.erase(i);
+    for (SocketClient* c : snapshotClients()) {
         command->runSocketCommand(c);
         c->decRef();
     }
diff --git a/libsysutils/src/SocketListener_test.cpp b/libsysutils/src/SocketListener_test.cpp
new file mode 100644
index 0000000..d6bfd02
--- /dev/null
+++ b/libsysutils/src/SocketListener_test.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sysutils/FrameworkCommand.h>
+#include <sysutils/FrameworkListener.h>
+
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <gtest/gtest.h>
+
+using android::base::unique_fd;
+
+namespace {
+
+std::string testSocketPath() {
+    const testing::TestInfo* const test_info =
+            testing::UnitTest::GetInstance()->current_test_info();
+    return std::string(ANDROID_SOCKET_DIR "/") + std::string(test_info->test_case_name()) +
+           std::string(".") + std::string(test_info->name());
+}
+
+unique_fd serverSocket(const std::string& path) {
+    unlink(path.c_str());
+
+    unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+    EXPECT_GE(fd.get(), 0);
+
+    struct sockaddr_un addr = {.sun_family = AF_UNIX};
+    strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+    EXPECT_EQ(bind(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0)
+            << "bind() to " << path << " failed: " << strerror(errno);
+    EXPECT_EQ(android_get_control_socket(path.c_str()), -1);
+
+    return fd;
+}
+
+unique_fd clientSocket(const std::string& path) {
+    unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+    EXPECT_GE(fd.get(), 0);
+
+    struct sockaddr_un addr = {.sun_family = AF_UNIX};
+    strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+    EXPECT_EQ(0, connect(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))
+            << "connect() to " << path << " failed: " << strerror(errno);
+
+    return fd;
+}
+
+void sendCmd(int fd, const char* cmd) {
+    EXPECT_TRUE(android::base::WriteFully(fd, cmd, strlen(cmd) + 1))
+            << "write() to socket failed: " << strerror(errno);
+}
+
+std::string recvReply(int fd) {
+    pollfd fds = {.fd = fd, .events = POLLIN};
+    int poll_events = poll(&fds, 1, -1);
+    EXPECT_EQ(1, poll_events);
+
+    // Technically, this one-shot read() is incorrect: we should keep on
+    // reading the socket until we get a \0. But this is also how
+    // FrameworkListener::onDataAvailable() reads, and it works because
+    // replies are always send with a single write() call, and clients
+    // always read replies before queueing the next command.
+    char buf[1024];
+    ssize_t len = read(fd, buf, sizeof(buf));
+    EXPECT_GE(len, 0) << "read() from socket failed: " << strerror(errno);
+    return len > 0 ? std::string(buf, buf + len) : "";
+}
+
+// Test command which echoes back all its arguments as a comma-separated list.
+// Always returns error code 42
+//
+// TODO: enable testing replies with addErrno=true and useCmdNum=true
+class TestCommand : public FrameworkCommand {
+  public:
+    TestCommand() : FrameworkCommand("test") {}
+    ~TestCommand() override {}
+
+    int runCommand(SocketClient* cli, int argc, char** argv) {
+        std::vector<std::string> args(argv, argv + argc);
+        std::string reply = android::base::Join(args, ',');
+        cli->sendMsg(42, reply.c_str(), /*addErrno=*/false, /*useCmdNum=*/false);
+        return 0;
+    }
+};
+
+// A test listener with a single command.
+class TestListener : public FrameworkListener {
+  public:
+    TestListener(int fd) : FrameworkListener(fd) {
+        registerCmd(new TestCommand);  // Leaked :-(
+    }
+};
+
+}  // unnamed namespace
+
+class FrameworkListenerTest : public testing::Test {
+  public:
+    FrameworkListenerTest() {
+        mSocketPath = testSocketPath();
+        mSserverFd = serverSocket(mSocketPath);
+        mListener = std::make_unique<TestListener>(mSserverFd.get());
+        EXPECT_EQ(0, mListener->startListener());
+    }
+
+    ~FrameworkListenerTest() override {
+        EXPECT_EQ(0, mListener->stopListener());
+
+        // Wouldn't it be cool if unique_fd had an option for taking care of this?
+        unlink(mSocketPath.c_str());
+    }
+
+    void testCommand(const char* command, const char* expected) {
+        unique_fd client_fd = clientSocket(mSocketPath);
+        sendCmd(client_fd.get(), command);
+
+        std::string reply = recvReply(client_fd.get());
+        EXPECT_EQ(std::string(expected) + '\0', reply);
+    }
+
+  protected:
+    std::string mSocketPath;
+    unique_fd mSserverFd;
+    std::unique_ptr<TestListener> mListener;
+};
+
+TEST_F(FrameworkListenerTest, DoesNothing) {
+    // Let the test harness start and stop a FrameworkListener
+    // without sending any commands through it.
+}
+
+TEST_F(FrameworkListenerTest, DispatchesValidCommands) {
+    testCommand("test", "42 test");
+    testCommand("test arg1 arg2", "42 test,arg1,arg2");
+    testCommand("test \"arg1 still_arg1\" arg2", "42 test,arg1 still_arg1,arg2");
+    testCommand("test \"escaped quote: '\\\"'\"", "42 test,escaped quote: '\"'");
+
+    // Perhaps this behavior was unintended, but would be good to detect any
+    // changes, in case anyone depends on it.
+    testCommand("test   ", "42 test,,,");
+}
+
+TEST_F(FrameworkListenerTest, RejectsInvalidCommands) {
+    testCommand("unknown arg1 arg2", "500 Command not recognized");
+    testCommand("test \"arg1 arg2", "500 Unclosed quotes error");
+    testCommand("test \\a", "500 Unsupported escape sequence");
+}
+
+TEST_F(FrameworkListenerTest, MultipleClients) {
+    unique_fd client1 = clientSocket(mSocketPath);
+    unique_fd client2 = clientSocket(mSocketPath);
+    sendCmd(client1.get(), "test 1");
+    sendCmd(client2.get(), "test 2");
+
+    EXPECT_EQ(std::string("42 test,2") + '\0', recvReply(client2.get()));
+    EXPECT_EQ(std::string("42 test,1") + '\0', recvReply(client1.get()));
+}
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index cd9ef61..0fa1638 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -21,6 +21,7 @@
 #include <type_traits>
 #include <vector>
 
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
 #include <unwindstack/DwarfError.h>
@@ -154,13 +155,15 @@
       break;
     case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
       *cur_pc += value;
-    // Fall through to log the value.
+      FALLTHROUGH_INTENDED;
+      // Fall through to log the value.
     case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
       string += " " + std::to_string(value);
       break;
     case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
       *cur_pc = value;
-    // Fall through to log the value.
+      FALLTHROUGH_INTENDED;
+      // Fall through to log the value.
     case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
       if (std::is_same<AddressType, uint32_t>::value) {
         string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 6061f61..57a780e 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -461,6 +461,7 @@
       if (reg == eval_info->cie->return_address_register) {
         eval_info->return_address_undefined = true;
       }
+      break;
     default:
       break;
   }
diff --git a/libutils/Android.bp b/libutils/Android.bp
index d635e65..600c91c 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -58,8 +58,8 @@
         "-Wall",
         "-Werror",
     ],
-    include_dirs: ["external/safe-iop/include"],
     header_libs: [
+        "libbase_headers",
         "libutils_headers",
     ],
     export_header_lib_headers: [
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 3f1e79a..ae10789 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -19,6 +19,8 @@
 
 #include <memory>
 
+#include <android-base/macros.h>
+
 #include <utils/RefBase.h>
 
 #include <utils/CallStack.h>
@@ -479,7 +481,7 @@
     case INITIAL_STRONG_VALUE:
         refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
                 std::memory_order_relaxed);
-        // fall through...
+        FALLTHROUGH_INTENDED;
     case 0:
         refs->mBase->onFirstRef();
     }
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index e00fb81..5f0a51f 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -16,8 +16,9 @@
 
 #define LOG_TAG "unicode"
 
-#include <utils/Unicode.h>
+#include <android-base/macros.h>
 #include <limits.h>
+#include <utils/Unicode.h>
 
 #include <log/log.h>
 
@@ -105,8 +106,11 @@
     switch (bytes)
     {   /* note: everything falls through. */
         case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            FALLTHROUGH_INTENDED;
         case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
     }
 }
@@ -340,27 +344,6 @@
            : 0);
 }
 
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2)
-{
-    const char16_t* e1 = s1H+n1;
-    const char16_t* e2 = s2N+n2;
-
-    while (s1H < e1 && s2N < e2) {
-        const char16_t c2 = ntohs(*s2N);
-        const int d = (int)*s1H++ - (int)c2;
-        s2N++;
-        if (d) {
-            return d;
-        }
-    }
-
-    return n1 < n2
-        ? (0 - (int)ntohs(*s2N))
-        : (n1 > n2
-           ? ((int)*s1H - 0)
-           : 0);
-}
-
 void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
 {
     if (src == nullptr || src_len == 0 || dst == nullptr) {
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 00a904d..e16f88d 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -24,8 +24,6 @@
 
 #include <log/log.h>
 
-#include <safe_iop.h>
-
 #include "SharedBuffer.h"
 
 /*****************************************************************************/
@@ -342,7 +340,7 @@
     }
 
     size_t new_allocation_size = 0;
-    LOG_ALWAYS_FATAL_IF(!safe_mul(&new_allocation_size, new_capacity, mItemSize));
+    LOG_ALWAYS_FATAL_IF(__builtin_mul_overflow(new_capacity, mItemSize, &new_allocation_size));
     SharedBuffer* sb = SharedBuffer::alloc(new_allocation_size);
     if (sb) {
         void* array = sb->data();
@@ -386,7 +384,7 @@
             this, (int)where, (int)amount, (int)mCount); // caller already checked
 
     size_t new_size;
-    LOG_ALWAYS_FATAL_IF(!safe_add(&new_size, mCount, amount), "new_size overflow");
+    LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(mCount, amount, &new_size), "new_size overflow");
 
     if (capacity() < new_size) {
         // NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2)
@@ -397,17 +395,18 @@
         //
         // This approximates the old calculation, using (x + (x/2) + 1) instead.
         size_t new_capacity = 0;
-        LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_size, (new_size / 2)),
+        LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(new_size, (new_size / 2), &new_capacity),
                             "new_capacity overflow");
-        LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_capacity, static_cast<size_t>(1u)),
-                            "new_capacity overflow");
+        LOG_ALWAYS_FATAL_IF(
+                __builtin_add_overflow(new_capacity, static_cast<size_t>(1u), &new_capacity),
+                "new_capacity overflow");
         new_capacity = max(kMinVectorCapacity, new_capacity);
 
         size_t new_alloc_size = 0;
-        LOG_ALWAYS_FATAL_IF(!safe_mul(&new_alloc_size, new_capacity, mItemSize),
+        LOG_ALWAYS_FATAL_IF(__builtin_mul_overflow(new_capacity, mItemSize, &new_alloc_size),
                             "new_alloc_size overflow");
 
-//        ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
+        // ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
         if ((mStorage) &&
             (mCount==where) &&
             (mFlags & HAS_TRIVIAL_COPY) &&
@@ -464,7 +463,7 @@
             this, (int)where, (int)amount, (int)mCount); // caller already checked
 
     size_t new_size;
-    LOG_ALWAYS_FATAL_IF(!safe_sub(&new_size, mCount, amount));
+    LOG_ALWAYS_FATAL_IF(__builtin_sub_overflow(mCount, amount, &new_size));
 
     if (new_size < (capacity() / 2)) {
         // NOTE: (new_size * 2) is safe because capacity didn't overflow and
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 360fce5..3abce17 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -228,8 +228,10 @@
 
 template<typename T>
 void sp<T>::clear() {
-    if (m_ptr) {
-        m_ptr->decStrong(this);
+    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
+    if (oldPtr) {
+        oldPtr->decStrong(this);
+        if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
         m_ptr = nullptr;
     }
 }
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index 666b70f..61a1b4f 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -40,9 +40,6 @@
 // equivalent result as strcmp16 (unlike strncmp16).
 int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
 
-// Version of strzcmp16 for comparing strings in different endianness.
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
-
 // Standard string functions on char32_t strings.
 size_t strlen32(const char32_t *);
 size_t strnlen32(const char32_t *, size_t);
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 2606aa9..fd3f602 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -158,4 +158,5 @@
         "libbase",
         "libziparchive",
     ],
+    recovery_available: true,
 }
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 9536fc7..f8d1356 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -41,6 +41,7 @@
 #include <android-base/logging.h>
 #include <android-base/macros.h>  // TEMP_FAILURE_RETRY may or may not be in unistd
 #include <android-base/memory.h>
+#include <android-base/utf8.h>
 #include <log/log.h>
 #include <utils/Compat.h>
 #include <utils/FileMap.h>
@@ -121,21 +122,36 @@
 #endif
 }
 
+static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
+                             const ZipStringOffset& zip_string_offset) {
+  const ZipString from_offset = zip_string_offset.GetZipString(start);
+  return from_offset == zip_string;
+}
+
+/**
+ * Returns offset of ZipString#name from the start of the central directory in the memory map.
+ * For valid ZipStrings contained in the zip archive mmap, 0 < offset < 0xffffff.
+ */
+static inline uint32_t GetOffset(const uint8_t* name, const uint8_t* start) {
+  CHECK_GT(name, start);
+  CHECK_LT(name, start + 0xffffff);
+  return static_cast<uint32_t>(name - start);
+}
+
 /*
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipString* hash_table, const uint32_t hash_table_size,
-                            const ZipString& name) {
+static int64_t EntryToIndex(const ZipStringOffset* hash_table, const uint32_t hash_table_size,
+                            const ZipString& name, const uint8_t* start) {
   const uint32_t hash = ComputeHash(name);
 
   // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
   uint32_t ent = hash & (hash_table_size - 1);
-  while (hash_table[ent].name != NULL) {
-    if (hash_table[ent] == name) {
+  while (hash_table[ent].name_offset != 0) {
+    if (isZipStringEqual(start, name, hash_table[ent])) {
       return ent;
     }
-
     ent = (ent + 1) & (hash_table_size - 1);
   }
 
@@ -146,8 +162,8 @@
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipString* hash_table, const uint64_t hash_table_size,
-                         const ZipString& name) {
+static int32_t AddToHash(ZipStringOffset* hash_table, const uint64_t hash_table_size,
+                         const ZipString& name, const uint8_t* start) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
 
@@ -155,20 +171,26 @@
    * We over-allocated the table, so we're guaranteed to find an empty slot.
    * Further, we guarantee that the hashtable size is not 0.
    */
-  while (hash_table[ent].name != NULL) {
-    if (hash_table[ent] == name) {
+  while (hash_table[ent].name_offset != 0) {
+    if (isZipStringEqual(start, name, hash_table[ent])) {
       // We've found a duplicate entry. We don't accept it
       ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
       return kDuplicateEntry;
     }
     ent = (ent + 1) & (hash_table_size - 1);
   }
-
-  hash_table[ent].name = name.name;
+  hash_table[ent].name_offset = GetOffset(name.name, start);
   hash_table[ent].name_length = name.name_length;
   return 0;
 }
 
+#if defined(__BIONIC__)
+uint64_t GetOwnerTag(const ZipArchive* archive) {
+  return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_ZIPARCHIVE,
+                                        reinterpret_cast<uint64_t>(archive));
+}
+#endif
+
 ZipArchive::ZipArchive(const int fd, bool assume_ownership)
     : mapped_zip(fd),
       close_file(assume_ownership),
@@ -180,7 +202,7 @@
       hash_table(nullptr) {
 #if defined(__BIONIC__)
   if (assume_ownership) {
-    android_fdsan_exchange_owner_tag(fd, 0, reinterpret_cast<uint64_t>(this));
+    android_fdsan_exchange_owner_tag(fd, 0, GetOwnerTag(this));
   }
 #endif
 }
@@ -198,7 +220,7 @@
 ZipArchive::~ZipArchive() {
   if (close_file && mapped_zip.GetFileDescriptor() >= 0) {
 #if defined(__BIONIC__)
-    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), reinterpret_cast<uint64_t>(this));
+    android_fdsan_close_with_tag(mapped_zip.GetFileDescriptor(), GetOwnerTag(this));
 #else
     close(mapped_zip.GetFileDescriptor());
 #endif
@@ -208,7 +230,8 @@
 }
 
 static int32_t MapCentralDirectory0(const char* debug_file_name, ZipArchive* archive,
-                                    off64_t file_length, off64_t read_amount, uint8_t* scan_buffer) {
+                                    off64_t file_length, off64_t read_amount,
+                                    uint8_t* scan_buffer) {
   const off64_t search_start = file_length - read_amount;
 
   if (!archive->mapped_zip.ReadAtOffset(scan_buffer, read_amount, search_start)) {
@@ -361,7 +384,7 @@
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
   archive->hash_table =
-      reinterpret_cast<ZipString*>(calloc(archive->hash_table_size, sizeof(ZipString)));
+      reinterpret_cast<ZipStringOffset*>(calloc(archive->hash_table_size, sizeof(ZipStringOffset)));
   if (archive->hash_table == nullptr) {
     ALOGW("Zip: unable to allocate the %u-entry hash_table, entry size: %zu",
           archive->hash_table_size, sizeof(ZipString));
@@ -417,7 +440,8 @@
     ZipString entry_name;
     entry_name.name = file_name;
     entry_name.name_length = file_name_length;
-    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name);
+    const int add_result = AddToHash(archive->hash_table, archive->hash_table_size, entry_name,
+                                     archive->central_directory.GetBasePtr());
     if (add_result != 0) {
       ALOGW("Zip: Error adding entry to hash table %d", add_result);
       return add_result;
@@ -471,7 +495,7 @@
 }
 
 int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
-  const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
+  const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY, 0);
   ZipArchive* archive = new ZipArchive(fd, true);
   *handle = archive;
 
@@ -537,7 +561,9 @@
   // Recover the start of the central directory entry from the filename
   // pointer.  The filename is the first entry past the fixed-size data,
   // so we can just subtract back from that.
-  const uint8_t* ptr = archive->hash_table[ent].name;
+  const ZipString from_offset =
+      archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+  const uint8_t* ptr = from_offset.name;
   ptr -= sizeof(CentralDirectoryRecord);
 
   // This is the base of our mmapped region, we have to sanity check that
@@ -647,8 +673,9 @@
       ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
       return kIoError;
     }
-
-    if (memcmp(archive->hash_table[ent].name, name_buf.data(), nameLen)) {
+    const ZipString from_offset =
+        archive->hash_table[ent].GetZipString(archive->central_directory.GetBasePtr());
+    if (memcmp(from_offset.name, name_buf.data(), nameLen)) {
       return kInconsistentInformation;
     }
 
@@ -746,19 +773,19 @@
     return kInvalidEntryName;
   }
 
-  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName);
-
+  const int64_t ent = EntryToIndex(archive->hash_table, archive->hash_table_size, entryName,
+                                   archive->central_directory.GetBasePtr());
   if (ent < 0) {
     ALOGV("Zip: Could not find entry %.*s", entryName.name_length, entryName.name);
     return ent;
   }
-
   return FindEntry(archive, ent, data);
 }
 
 int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
+    ALOGW("Zip: Null ZipArchiveHandle");
     return kInvalidHandle;
   }
 
@@ -770,19 +797,19 @@
 
   const uint32_t currentOffset = handle->position;
   const uint32_t hash_table_length = archive->hash_table_size;
-  const ZipString* hash_table = archive->hash_table;
-
+  const ZipStringOffset* hash_table = archive->hash_table;
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
-    if (hash_table[i].name != NULL &&
-        (handle->prefix.name_length == 0 || hash_table[i].StartsWith(handle->prefix)) &&
-        (handle->suffix.name_length == 0 || hash_table[i].EndsWith(handle->suffix))) {
+    const ZipString from_offset =
+        hash_table[i].GetZipString(archive->central_directory.GetBasePtr());
+    if (hash_table[i].name_offset != 0 &&
+        (handle->prefix.name_length == 0 || from_offset.StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 || from_offset.EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
       if (!error) {
-        name->name = hash_table[i].name;
+        name->name = from_offset.name;
         name->name_length = hash_table[i].name_length;
       }
-
       return error;
     }
   }
diff --git a/libziparchive/zip_archive_private.h b/libziparchive/zip_archive_private.h
index 0a73300..83cb11f 100644
--- a/libziparchive/zip_archive_private.h
+++ b/libziparchive/zip_archive_private.h
@@ -136,6 +136,26 @@
   size_t length_;
 };
 
+/**
+ * More space efficient string representation of strings in an mmaped zipped file than
+ * std::string_view or ZipString. Using ZipString as an entry in the ZipArchive hashtable wastes
+ * space. ZipString stores a pointer to a string (on 64 bit, 8 bytes) and the length to read from
+ * that pointer, 2 bytes. Because of alignment, the structure consumes 16 bytes, wasting 6 bytes.
+ * ZipStringOffset stores a 4 byte offset from a fixed location in the memory mapped file instead
+ * of the entire address, consuming 8 bytes with alignment.
+ */
+struct ZipStringOffset {
+  uint32_t name_offset;
+  uint16_t name_length;
+
+  const ZipString GetZipString(const uint8_t* start) const {
+    ZipString zip_string;
+    zip_string.name = start + name_offset;
+    zip_string.name_length = name_length;
+    return zip_string;
+  }
+};
+
 struct ZipArchive {
   // open Zip archive
   mutable MappedZipFile mapped_zip;
@@ -154,7 +174,7 @@
   // allocate so the maximum number entries can never be higher than
   // ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
   uint32_t hash_table_size;
-  ZipString* hash_table;
+  ZipStringOffset* hash_table;
 
   ZipArchive(const int fd, bool assume_ownership);
   ZipArchive(void* address, size_t length);
diff --git a/llkd/Android.bp b/llkd/Android.bp
index a6edd26..62a637d 100644
--- a/llkd/Android.bp
+++ b/llkd/Android.bp
@@ -20,6 +20,12 @@
     export_include_dirs: ["include"],
 
     cflags: ["-Werror"],
+
+    product_variables: {
+        debuggable: {
+            cppflags: ["-D__PTRACE_ENABLED__"],
+        },
+    },
 }
 
 cc_binary {
@@ -39,4 +45,9 @@
     cflags: ["-Werror"],
 
     init_rc: ["llkd.rc"],
+    product_variables: {
+        debuggable: {
+            init_rc: ["llkd-debuggable.rc"],
+	},
+    },
 }
diff --git a/llkd/README.md b/llkd/README.md
index 2314583..1f69718 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -23,6 +23,9 @@
 Operations
 ----------
 
+There are two detection scenarios. Persistent D or Z state, and persistent
+stack signature.
+
 If a thread is in D or Z state with no forward progress for longer than
 ro.llk.timeout_ms, or ro.llk.[D|Z].timeout_ms, kill the process or parent
 process respectively.  If another scan shows the same process continues to
@@ -32,6 +35,26 @@
 double the expected time to flow through the mainloop.  Sampling is every
 ro.llk_sample_ms.
 
+For usedebug releases only, persistent stack signature checking is enabled.
+If a thread in any state but Z, has a persistent listed ro.llk.stack kernel
+symbol always being reported, even if there is forward scheduling progress, for
+longer than ro.llk.timeout_ms, or ro.llk.stack.timeout_ms, then issue a kill
+to the process.  If another scan shows the same process continues to exist,
+then have a confirmed live-lock condition and need to panic.  There is no
+ABA detection since forward scheduling progress is allowed, thus the condition
+for the symbols are:
+
+- Check is looking for " " + __symbol__+ "0x" in /proc/<pid>/stack.
+- The __symbol__ should be rare and short lived enough that on a typical
+  system the function is seen at most only once in a sample over the timeout
+  period of ro.llk.stack.timeout_ms, samples occur every ro.llk.check_ms. This
+  can be the only way to prevent a false trigger as there is no ABA protection.
+- Persistent continuously when the live lock condition exists.
+- Should be just below the function that is calling the lock that could
+  contend, because if the lock is below or in the symbol function, the
+  symbol will show in all affected processes, not just the one that
+  caused the lockup.
+
 Default will not monitor init, or [kthreadd] and all that [kthreadd] spawns.
 This reduces the effectiveness of llkd by limiting its coverage.  If there is
 value in covering [kthreadd] spawned threads, the requirement will be that
@@ -40,7 +63,9 @@
 coding hygiene, a common request to add such to publicly reviewed kernel.org
 maintained drivers).  For instance use wait_event_interruptible() instead of
 wait_event().  The blacklists can be adjusted accordingly if these
-conditions are met to cover kernel components.
+conditions are met to cover kernel components.  For the stack symbol checking,
+there is an additional process blacklist so that we do not incide sepolicy
+violations on services that block ptrace operations.
 
 An accompanying gTest set have been added, and will setup a persistent D or Z
 process, with and without forward progress, but not in a live-lock state
@@ -93,14 +118,31 @@
 #### ro.llk.Z.timeout_ms
 default ro.llk.timeout_ms, Z maximum timelimit.
 
+#### ro.llk.stack.timeout_ms
+default ro.llk.timeout_ms,
+checking for persistent stack symbols maximum timelimit.
+Only active on userdebug and eng builds.
+
 #### ro.llk.check_ms
 default 2 minutes samples of threads for D or Z.
 
+#### ro.llk.stack
+default *empty* or false, comma separated list of kernel symbols.
+The string "*false*" is the equivalent to an *empty* list.
+Look for kernel stack symbols that if ever persistently present can
+indicate a subsystem is locked up.
+Beware, check does not on purpose do forward scheduling ABA except by polling
+every ro.llk_check_ms over the period ro.llk.stack.timeout_ms, so stack symbol
+should be exceptionally rare and fleeting.
+One must be convinced that it is virtually *impossible* for symbol to show up
+persistently in all samples of the stack.
+Only active on userdebug and eng builds.
+
 #### ro.llk.blacklist.process
 default 0,1,2 (kernel, init and [kthreadd]) plus process names
 init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
 [watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
-The string false is the equivalent to an empty list.
+The string "*false*" is the equivalent to an *empty* list.
 Do not watch these processes.  A process can be comm, cmdline or pid reference.
 NB: automated default here can be larger than the current maximum property
 size of 92.
@@ -108,15 +150,23 @@
 
 #### ro.llk.blacklist.parent
 default 0,2 (kernel and [kthreadd]).
-The string false is the equivalent to an empty list.
+The string "*false*" is the equivalent to an *empty* list.
 Do not watch processes that have this parent.
 A parent process can be comm, cmdline or pid reference.
 
 #### ro.llk.blacklist.uid
 default *empty* or false, comma separated list of uid numbers or names.
-The string false is the equivalent to an empty list.
+The string "*false*" is the equivalent to an *empty* list.
 Do not watch processes that match this uid.
 
+#### ro.llk.blacklist.process.stack
+default process names init,lmkd,lmkd.llkd,llkd,keystore,logd.
+The string "*false*" is the equivalent to an *empty* list.
+This subset of processes are not monitored for live lock stack signatures.
+Also prevents the sepolicy violation associated with processes that block
+ptrace, as these can not be checked anyways.
+Only active on userdebug and eng builds.
+
 Architectural Concerns
 ----------------------
 
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index e3ae4bb..d0188ec 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -43,9 +43,12 @@
 #define KHT_TIMEOUT_PROPERTY           "ro.khungtask.timeout"
 #define LLK_D_TIMEOUT_MS_PROPERTY      "ro.llk.D.timeout_ms"
 #define LLK_Z_TIMEOUT_MS_PROPERTY      "ro.llk.Z.timeout_ms"
+#define LLK_STACK_TIMEOUT_MS_PROPERTY  "ro.llk.stack.timeout_ms"
 #define LLK_CHECK_MS_PROPERTY          "ro.llk.check_ms"
 /* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
 #define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
+#define LLK_CHECK_STACK_PROPERTY       "ro.llk.stack"
+#define LLK_CHECK_STACK_DEFAULT        ""
 #define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
 #define LLK_BLACKLIST_PROCESS_DEFAULT  \
     "0,1,2,init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
@@ -53,6 +56,8 @@
 #define LLK_BLACKLIST_PARENT_DEFAULT   "0,2,[kthreadd]"
 #define LLK_BLACKLIST_UID_PROPERTY     "ro.llk.blacklist.uid"
 #define LLK_BLACKLIST_UID_DEFAULT      ""
+#define LLK_BLACKLIST_STACK_PROPERTY   "ro.llk.blacklist.process.stack"
+#define LLK_BLACKLIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,/system/bin/keystore"
 /* clang-format on */
 
 __END_DECLS
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 48551f2..58c2ba8 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -73,7 +73,14 @@
 bool llkMlockall = LLK_MLOCKALL_DEFAULT;             // run mlocked
 bool llkTestWithKill = LLK_KILLTEST_DEFAULT;         // issue test kills
 milliseconds llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;  // default timeout
-enum { llkStateD, llkStateZ, llkNumStates };         // state indexes
+enum {                                               // enum of state indexes
+    llkStateD,                                       // Persistent 'D' state
+    llkStateZ,                                       // Persistent 'Z' state
+#ifdef __PTRACE_ENABLED__                            // Extra privileged states
+    llkStateStack,                                   // stack signature
+#endif                                               // End of extra privilege
+    llkNumStates,                                    // Maxumum number of states
+};                                                   // state indexes
 milliseconds llkStateTimeoutMs[llkNumStates];        // timeout override for each detection state
 milliseconds llkCheckMs;                             // checking interval to inspect any
                                                      // persistent live-locked states
@@ -83,6 +90,10 @@
 // Provides a wide angle of margin b/c khtTimeout is also its granularity.
 seconds khtTimeout = duration_cast<seconds>(llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) /
                                             LLK_CHECKS_PER_TIMEOUT_DEFAULT);
+#ifdef __PTRACE_ENABLED__
+// list of stack symbols to search for persistence.
+std::unordered_set<std::string> llkCheckStackSymbols;
+#endif
 
 // Blacklist variables, initialized with comma separated lists of high false
 // positive and/or dangerous references, e.g. without self restart, for pid,
@@ -97,6 +108,11 @@
 std::unordered_set<std::string> llkBlacklistParent;
 // list of uids, and uid names, to skip, default nothing
 std::unordered_set<std::string> llkBlacklistUid;
+#ifdef __PTRACE_ENABLED__
+// list of names to skip stack checking. "init", "lmkd", "llkd", "keystore" or
+// "logd" (if not userdebug).
+std::unordered_set<std::string> llkBlacklistStack;
+#endif
 
 class dir {
   public:
@@ -263,6 +279,9 @@
                                    // forward scheduling progress.
     milliseconds update;           // llkUpdate millisecond signature of last.
     milliseconds count;            // duration in state.
+#ifdef __PTRACE_ENABLED__          // Privileged state checking
+    milliseconds count_stack;      // duration where stack is stagnant.
+#endif                             // End privilege
     pid_t pid;                     // /proc/<pid> before iterating through
                                    // /proc/<pid>/task/<tid> for threads.
     pid_t ppid;                    // /proc/<tid>/stat field 4 parent pid.
@@ -272,6 +291,9 @@
     std::string cmdline;           // cached /cmdline content
     char state;                    // /proc/<tid>/stat field 3: Z or D
                                    // (others we do not monitor: S, R, T or ?)
+#ifdef __PTRACE_ENABLED__          // Privileged state checking
+    char stack;                    // index in llkCheckStackSymbols for matches
+#endif                             // and with maximum index PROP_VALUE_MAX/2.
     char comm[TASK_COMM_LEN + 3];  // space for adding '[' and ']'
     bool exeMissingValid;          // exeMissing has been cached
     bool cmdlineValid;             // cmdline has been cached
@@ -286,11 +308,17 @@
           nrSwitches(0),
           update(llkUpdate),
           count(0ms),
+#ifdef __PTRACE_ENABLED__
+          count_stack(0ms),
+#endif
           pid(pid),
           ppid(ppid),
           uid(-1),
           time(time),
           state(state),
+#ifdef __PTRACE_ENABLED__
+          stack(-1),
+#endif
           exeMissingValid(false),
           cmdlineValid(false),
           updated(true),
@@ -343,6 +371,10 @@
     void reset(void) {  // reset cache, if we detected pid rollover
         uid = -1;
         state = '?';
+#ifdef __PTRACE_ENABLED__
+        count_stack = 0ms;
+        stack = -1;
+#endif
         cmdline = "";
         comm[0] = '\0';
         exeMissingValid = false;
@@ -667,6 +699,48 @@
     return ret;
 }
 
+#ifdef __PTRACE_ENABLED__
+bool llkCheckStack(proc* procp, const std::string& piddir) {
+    if (llkCheckStackSymbols.empty()) return false;
+    if (procp->state == 'Z') {  // No brains for Zombies
+        procp->stack = -1;
+        procp->count_stack = 0ms;
+        return false;
+    }
+
+    // Don't check process that are known to block ptrace, save sepolicy noise.
+    if (llkSkipName(std::to_string(procp->pid), llkBlacklistStack)) return false;
+    if (llkSkipName(procp->getComm(), llkBlacklistStack)) return false;
+    if (llkSkipName(procp->getCmdline(), llkBlacklistStack)) return false;
+
+    auto kernel_stack = ReadFile(piddir + "/stack");
+    if (kernel_stack.empty()) {
+        LOG(INFO) << piddir << "/stack empty comm=" << procp->getComm()
+                  << " cmdline=" << procp->getCmdline();
+        return false;
+    }
+    // A scheduling incident that should not reset count_stack
+    if (kernel_stack.find(" cpu_worker_pools+0x") != std::string::npos) return false;
+    char idx = -1;
+    char match = -1;
+    for (const auto& stack : llkCheckStackSymbols) {
+        if (++idx < 0) break;
+        if (kernel_stack.find(" "s + stack + "+0x") != std::string::npos) {
+            match = idx;
+            break;
+        }
+    }
+    if (procp->stack != match) {
+        procp->stack = match;
+        procp->count_stack = 0ms;
+        return false;
+    }
+    if (match == char(-1)) return false;
+    procp->count_stack += llkCycle;
+    return procp->count_stack >= llkStateTimeoutMs[llkStateStack];
+}
+#endif
+
 // Primary ABA mitigation watching last time schedule activity happened
 void llkCheckSchedUpdate(proc* procp, const std::string& piddir) {
     // Audit finds /proc/<tid>/sched is just over 1K, and
@@ -731,13 +805,23 @@
               << LLK_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkTimeoutMs) << "\n"
               << LLK_D_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkStateTimeoutMs[llkStateD]) << "\n"
               << LLK_Z_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkStateTimeoutMs[llkStateZ]) << "\n"
+#ifdef __PTRACE_ENABLED__
+              << LLK_STACK_TIMEOUT_MS_PROPERTY "=" << llkFormat(llkStateTimeoutMs[llkStateStack])
+              << "\n"
+#endif
               << LLK_CHECK_MS_PROPERTY "=" << llkFormat(llkCheckMs) << "\n"
+#ifdef __PTRACE_ENABLED__
+              << LLK_CHECK_STACK_PROPERTY "=" << llkFormat(llkCheckStackSymbols) << "\n"
+              << LLK_BLACKLIST_STACK_PROPERTY "=" << llkFormat(llkBlacklistStack) << "\n"
+#endif
               << LLK_BLACKLIST_PROCESS_PROPERTY "=" << llkFormat(llkBlacklistProcess) << "\n"
               << LLK_BLACKLIST_PARENT_PROPERTY "=" << llkFormat(llkBlacklistParent) << "\n"
               << LLK_BLACKLIST_UID_PROPERTY "=" << llkFormat(llkBlacklistUid);
 }
 
 void* llkThread(void* obj) {
+    prctl(PR_SET_DUMPABLE, 0);
+
     LOG(INFO) << "started";
 
     std::string name = std::to_string(::gettid());
@@ -890,9 +974,14 @@
             if (pid == myPid) {
                 break;
             }
-            if (!llkIsMonitorState(state)) {
+#ifdef __PTRACE_ENABLED__
+            // if no stack monitoring, we can quickly exit here
+            if (!llkIsMonitorState(state) && llkCheckStackSymbols.empty()) {
                 continue;
             }
+#else
+            if (!llkIsMonitorState(state)) continue;
+#endif
             if ((tid == myTid) || llkSkipPid(tid)) {
                 continue;
             }
@@ -923,12 +1012,26 @@
             // ABA mitigation watching last time schedule activity happened
             llkCheckSchedUpdate(procp, piddir);
 
-            // Can only fall through to here if registered D or Z state !!!
-            if (procp->count < llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
-                LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
-                             << pid << "->" << tid << ' ' << procp->getComm();
+#ifdef __PTRACE_ENABLED__
+            auto stuck = llkCheckStack(procp, piddir);
+            if (llkIsMonitorState(state)) {
+                if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
+                    stuck = true;
+                } else if (procp->count != 0ms) {
+                    LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
+                                 << pid << "->" << tid << ' ' << procp->getComm();
+                }
+            }
+            if (!stuck) continue;
+#else
+            if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {
+                if (procp->count != 0ms) {
+                    LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->"
+                                 << pid << "->" << tid << ' ' << procp->getComm();
+                }
                 continue;
             }
+#endif
 
             // We have to kill it to determine difference between live lock
             // and persistent state blocked on a resource.  Is there something
@@ -967,12 +1070,13 @@
                         // not working is we kill a process that likes to
                         // stay in 'D' state, instead of panicing the
                         // kernel (worse).
-                        LOG(WARNING) << "D " << llkFormat(procp->count) << ' ' << pid << "->" << tid
-                                     << ' ' << procp->getComm() << " [kill]";
+                    default:
+                        LOG(WARNING) << state << ' ' << llkFormat(procp->count) << ' ' << pid
+                                     << "->" << tid << ' ' << procp->getComm() << " [kill]";
                         if ((llkKillOneProcess(llkTidLookup(pid), procp) >= 0) ||
-                            (llkKillOneProcess(pid, 'D', tid) >= 0) ||
+                            (llkKillOneProcess(pid, state, tid) >= 0) ||
                             (llkKillOneProcess(procp, procp) >= 0) ||
-                            (llkKillOneProcess(tid, 'D', tid) >= 0)) {
+                            (llkKillOneProcess(tid, state, tid) >= 0)) {
                             continue;
                         }
                         break;
@@ -981,7 +1085,8 @@
             // We are here because we have confirmed kernel live-lock
             LOG(ERROR) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->" << pid
                        << "->" << tid << ' ' << procp->getComm() << " [panic]";
-            llkPanicKernel(true, tid, (state == 'Z') ? "zombie" : "driver");
+            llkPanicKernel(true, tid,
+                           (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping");
         }
         LOG(VERBOSE) << "+closedir()";
     }
@@ -1039,8 +1144,9 @@
 }
 
 bool llkInit(const char* threadname) {
+    auto debuggable = android::base::GetBoolProperty("ro.debuggable", false);
     llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
-    if (!LLK_ENABLE_DEFAULT && android::base::GetBoolProperty("ro.debuggable", false)) {
+    if (!LLK_ENABLE_DEFAULT && debuggable) {
         llkEnable = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
         khtEnable = android::base::GetProperty(KHT_ENABLE_PROPERTY, "eng") == "eng";
     }
@@ -1067,8 +1173,21 @@
     llkValidate();  // validate llkTimeoutMs, llkCheckMs and llkCycle
     llkStateTimeoutMs[llkStateD] = GetUintProperty(LLK_D_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
     llkStateTimeoutMs[llkStateZ] = GetUintProperty(LLK_Z_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+#ifdef __PTRACE_ENABLED__
+    llkStateTimeoutMs[llkStateStack] = GetUintProperty(LLK_STACK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+#endif
     llkCheckMs = GetUintProperty(LLK_CHECK_MS_PROPERTY, llkCheckMs);
     llkValidate();  // validate all (effectively minus llkTimeoutMs)
+#ifdef __PTRACE_ENABLED__
+    if (debuggable) {
+        llkCheckStackSymbols = llkSplit(
+                android::base::GetProperty(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT));
+    }
+    std::string defaultBlacklistStack(LLK_BLACKLIST_STACK_DEFAULT);
+    if (!debuggable) defaultBlacklistStack += ",logd,/system/bin/logd";
+    llkBlacklistStack = llkSplit(
+            android::base::GetProperty(LLK_BLACKLIST_STACK_PROPERTY, defaultBlacklistStack));
+#endif
     std::string defaultBlacklistProcess(
         std::to_string(kernelPid) + "," + std::to_string(initPid) + "," +
         std::to_string(kthreaddPid) + "," + std::to_string(::getpid()) + "," +
diff --git a/llkd/llkd-debuggable.rc b/llkd/llkd-debuggable.rc
new file mode 100644
index 0000000..724cb5e
--- /dev/null
+++ b/llkd/llkd-debuggable.rc
@@ -0,0 +1,19 @@
+on property:ro.debuggable=1
+    setprop llk.enable ${ro.llk.enable:-1}
+    setprop khungtask.enable ${ro.khungtask.enable:-1}
+
+on property:ro.llk.enable=eng
+    setprop llk.enable ${ro.debuggable:-0}
+
+on property:ro.khungtask.enable=eng
+    setprop khungtask.enable ${ro.debuggable:-0}
+
+service llkd-1 /system/bin/llkd
+    class late_start
+    disabled
+    user llkd
+    group llkd readproc
+    capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE
+    file /dev/kmsg w
+    file /proc/sysrq-trigger w
+    writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/llkd.cpp b/llkd/llkd.cpp
index f10253d..1920198 100644
--- a/llkd/llkd.cpp
+++ b/llkd/llkd.cpp
@@ -17,6 +17,7 @@
 #include "llkd.h"
 
 #include <sched.h>
+#include <sys/prctl.h>
 #include <unistd.h>
 
 #include <chrono>
@@ -26,6 +27,8 @@
 using namespace std::chrono;
 
 int main(int, char**) {
+    prctl(PR_SET_DUMPABLE, 0);
+
     LOG(INFO) << "started";
 
     bool enabled = llkInit();
diff --git a/llkd/llkd.rc b/llkd/llkd.rc
index e538cdb..b1f96a8 100644
--- a/llkd/llkd.rc
+++ b/llkd/llkd.rc
@@ -3,15 +3,8 @@
     setprop llk.enable ${ro.llk.enable:-0}
     setprop khungtask.enable ${ro.khungtask.enable:-0}
 
-on property:ro.debuggable=1
-    setprop llk.enable ${ro.llk.enable:-1}
-    setprop khungtask.enable ${ro.khungtask.enable:-1}
-
-on property:ro.llk.enable=eng
-    setprop llk.enable ${ro.debuggable:-0}
-
-on property:ro.khungtask.enable=eng
-    setprop khungtask.enable ${ro.debuggable:-0}
+on property:ro.llk.enable=true
+    setprop llk.enable true
 
 on property:llk.enable=1
     setprop llk.enable true
@@ -19,6 +12,9 @@
 on property:llk.enable=0
     setprop llk.enable false
 
+on property:ro.khungtask.enable=true
+    setprop khungtask.enable true
+
 on property:khungtask.enable=1
     setprop khungtask.enable true
 
@@ -36,9 +32,9 @@
     write /proc/sys/kernel/hung_task_panic 0
 
 on property:llk.enable=true
-    start llkd
+    start llkd-${ro.debuggable:-0}
 
-service llkd /system/bin/llkd
+service llkd-0 /system/bin/llkd
     class late_start
     disabled
     user llkd
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
index 3a15ff1..f54932b 100644
--- a/llkd/tests/llkd_test.cpp
+++ b/llkd/tests/llkd_test.cpp
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
 #include <signal.h>
 #include <stdint.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -81,9 +83,12 @@
         (GetUintProperty(LLK_CHECK_MS_PROPERTY,
                          LLK_TIMEOUT_MS_DEFAULT / LLK_CHECKS_PER_TIMEOUT_DEFAULT) !=
          duration_cast<milliseconds>(10s))) {
-        execute("stop llkd");
+        execute("stop llkd-0");
+        execute("stop llkd-1");
         rest();
         std::string setprop("setprop ");
+        execute((setprop + LLK_CHECK_STACK_PROPERTY + " SyS_openat").c_str());
+        rest();
         execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " false").c_str());
         rest();
         execute((setprop + LLK_TIMEOUT_MS_PROPERTY + " 120000").c_str());
@@ -92,8 +97,10 @@
         rest();
         execute((setprop + LLK_CHECK_MS_PROPERTY + " 10000").c_str());
         rest();
-        execute((setprop + LLK_ENABLE_PROPERTY + " true").c_str());
-        rest();
+        if (!default_enable) {
+            execute((setprop + LLK_ENABLE_PROPERTY + " true").c_str());
+            rest();
+        }
         execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + " true").c_str());
         rest();
     }
@@ -104,7 +111,7 @@
     }
     default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);
     if (default_enable) {
-        execute("start llkd");
+        execute("start llkd-1");
         rest();
         GTEST_LOG_INFO << "llkd enabled\n";
     } else {
@@ -123,8 +130,10 @@
         llkTimeoutMs = LLK_TIMEOUT_MS_MINIMUM;
     }
     milliseconds llkCheckMs = llkTimeoutMs / LLK_CHECKS_PER_TIMEOUT_DEFAULT;
-    auto timeout = GetUintProperty(
-        (state == 'Z') ? LLK_Z_TIMEOUT_MS_PROPERTY : LLK_D_TIMEOUT_MS_PROPERTY, llkTimeoutMs);
+    auto timeout = GetUintProperty((state == 'Z') ? LLK_Z_TIMEOUT_MS_PROPERTY
+                                                  : (state == 'S') ? LLK_STACK_TIMEOUT_MS_PROPERTY
+                                                                   : LLK_D_TIMEOUT_MS_PROPERTY,
+                                   llkTimeoutMs);
     if (timeout < LLK_TIMEOUT_MS_MINIMUM) {
         timeout = LLK_TIMEOUT_MS_MINIMUM;
     }
@@ -285,3 +294,41 @@
 
     waitForPid(child_pid);
 }
+
+TEST(llkd, sleep) {
+    if (checkKill("kernel_panic,sysrq,livelock,sleeping")) {
+        return;
+    }
+    if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+        GTEST_LOG_WARNING << "Features not available on user builds\n";
+    }
+
+    const auto period = llkdSleepPeriod('S');
+
+    /* Create a Persistent SyS_openat for single-ended pipe */
+    static constexpr char stack_pipe_file[] = "/dev/stack_pipe_file";
+    unlink(stack_pipe_file);
+    auto pipe_ret = mknod(stack_pipe_file, S_IFIFO | 0666, 0);
+    ASSERT_LE(0, pipe_ret);
+
+    auto child_pid = fork();
+    ASSERT_LE(0, child_pid);
+    if (!child_pid) {
+        child_pid = fork();
+        ASSERT_LE(0, child_pid);
+        if (!child_pid) {
+            sleep(period.count());
+            auto fd = open(stack_pipe_file, O_RDONLY | O_CLOEXEC);
+            close(fd);
+            exit(0);
+        } else {
+            auto fd = open(stack_pipe_file, O_WRONLY | O_CLOEXEC);
+            close(fd);
+            exit(42);
+        }
+    }
+
+    waitForPid(child_pid);
+
+    unlink(stack_pipe_file);
+}
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 02534f2..1980dc6 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -31,6 +31,7 @@
 #include <sys/socket.h>
 #include <sys/sysinfo.h>
 #include <sys/types.h>
+#include <time.h>
 #include <unistd.h>
 
 #include <cutils/properties.h>
@@ -38,6 +39,7 @@
 #include <lmkd.h>
 #include <log/log.h>
 #include <log/log_event_list.h>
+#include <log/log_time.h>
 
 #ifdef LMKD_LOG_STATS
 #include "statslog.h"
@@ -83,6 +85,10 @@
 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
 #define EIGHT_MEGA (1 << 23)
 
+#define TARGET_UPDATE_MIN_INTERVAL_MS 1000
+
+#define NS_PER_MS (NS_PER_SEC / MS_PER_SEC)
+
 /* Defined as ProcessList.SYSTEM_ADJ in ProcessList.java */
 #define SYSTEM_ADJ (-900)
 
@@ -91,6 +97,8 @@
 
 #define min(a, b) (((a) < (b)) ? (a) : (b))
 
+#define FAIL_REPORT_RLIMIT_MS 1000
+
 /* default to old in-kernel interface if no memory pressure events */
 static bool use_inkernel_interface = true;
 static bool has_inkernel_module;
@@ -494,6 +502,12 @@
     return true;
 }
 
+static inline long get_time_diff_ms(struct timespec *from,
+                                    struct timespec *to) {
+    return (to->tv_sec - from->tv_sec) * (long)MS_PER_SEC +
+           (to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS;
+}
+
 static void cmd_procprio(LMKD_CTRL_PACKET packet) {
     struct proc *procp;
     char path[80];
@@ -604,18 +618,52 @@
 static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
     int i;
     struct lmk_target target;
+    char minfree_str[PROPERTY_VALUE_MAX];
+    char *pstr = minfree_str;
+    char *pend = minfree_str + sizeof(minfree_str);
+    static struct timespec last_req_tm;
+    struct timespec curr_tm;
 
-    if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
+    if (ntargets < 1 || ntargets > (int)ARRAY_SIZE(lowmem_adj))
         return;
 
+    /*
+     * Ratelimit minfree updates to once per TARGET_UPDATE_MIN_INTERVAL_MS
+     * to prevent DoS attacks
+     */
+    if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
+        ALOGE("Failed to get current time");
+        return;
+    }
+
+    if (get_time_diff_ms(&last_req_tm, &curr_tm) <
+        TARGET_UPDATE_MIN_INTERVAL_MS) {
+        ALOGE("Ignoring frequent updated to lmkd limits");
+        return;
+    }
+
+    last_req_tm = curr_tm;
+
     for (i = 0; i < ntargets; i++) {
         lmkd_pack_get_target(packet, i, &target);
         lowmem_minfree[i] = target.minfree;
         lowmem_adj[i] = target.oom_adj_score;
+
+        pstr += snprintf(pstr, pend - pstr, "%d:%d,", target.minfree,
+            target.oom_adj_score);
+        if (pstr >= pend) {
+            /* if no more space in the buffer then terminate the loop */
+            pstr = pend;
+            break;
+        }
     }
 
     lowmem_targets_size = ntargets;
 
+    /* Override the last extra comma */
+    pstr[-1] = '\0';
+    property_set("sys.lmk.minfree_levels", minfree_str);
+
     if (has_inkernel_module) {
         char minfreestr[128];
         char killpriostr[128];
@@ -1051,8 +1099,7 @@
 }
 
 /* Kill one process specified by procp.  Returns the size of the process killed */
-static int kill_one_process(struct proc* procp, int min_score_adj,
-                            enum vmpressure_level level) {
+static int kill_one_process(struct proc* procp) {
     int pid = procp->pid;
     uid_t uid = procp->uid;
     char *taskname;
@@ -1086,11 +1133,8 @@
 
     /* CAP_KILL required */
     r = kill(pid, SIGKILL);
-    ALOGI(
-        "Killing '%s' (%d), uid %d, adj %d\n"
-        "   to free %ldkB because system is under %s memory pressure (min_oom_adj=%d)\n",
-        taskname, pid, uid, procp->oomadj, tasksize * page_k,
-        level_name[level], min_score_adj);
+    ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB",
+        taskname, pid, uid, procp->oomadj, tasksize * page_k);
     pid_remove(pid);
 
     TRACE_KILL_END();
@@ -1117,8 +1161,7 @@
  * If pages_to_free is set to 0 only one process will be killed.
  * Returns the size of the killed processes.
  */
-static int find_and_kill_processes(enum vmpressure_level level,
-                                   int min_score_adj, int pages_to_free) {
+static int find_and_kill_processes(int min_score_adj, int pages_to_free) {
     int i;
     int killed_size;
     int pages_freed = 0;
@@ -1137,7 +1180,7 @@
             if (!procp)
                 break;
 
-            killed_size = kill_one_process(procp, min_score_adj, level);
+            killed_size = kill_one_process(procp);
             if (killed_size >= 0) {
 #ifdef LMKD_LOG_STATS
                 if (enable_stats_log && !lmk_state_change_start) {
@@ -1228,12 +1271,6 @@
         level - 1 : level);
 }
 
-static inline unsigned long get_time_diff_ms(struct timeval *from,
-                                             struct timeval *to) {
-    return (to->tv_sec - from->tv_sec) * 1000 +
-           (to->tv_usec - from->tv_usec) / 1000;
-}
-
 static void mp_event_common(int data, uint32_t events __unused) {
     int ret;
     unsigned long long evcount;
@@ -1242,8 +1279,9 @@
     enum vmpressure_level lvl;
     union meminfo mi;
     union zoneinfo zi;
-    static struct timeval last_report_tm;
-    static unsigned long skip_count = 0;
+    struct timespec curr_tm;
+    static struct timespec last_kill_tm;
+    static unsigned long kill_skip_count = 0;
     enum vmpressure_level level = (enum vmpressure_level)data;
     long other_free = 0, other_file = 0;
     int min_score_adj;
@@ -1272,19 +1310,22 @@
         }
     }
 
+    if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
+        ALOGE("Failed to get current time");
+        return;
+    }
+
     if (kill_timeout_ms) {
-        struct timeval curr_tm;
-        gettimeofday(&curr_tm, NULL);
-        if (get_time_diff_ms(&last_report_tm, &curr_tm) < kill_timeout_ms) {
-            skip_count++;
+        if (get_time_diff_ms(&last_kill_tm, &curr_tm) < kill_timeout_ms) {
+            kill_skip_count++;
             return;
         }
     }
 
-    if (skip_count > 0) {
+    if (kill_skip_count > 0) {
         ALOGI("%lu memory pressure events were skipped after a kill!",
-              skip_count);
-        skip_count = 0;
+              kill_skip_count);
+        kill_skip_count = 0;
     }
 
     if (meminfo_parse(&mi) < 0 || zoneinfo_parse(&zi) < 0) {
@@ -1380,7 +1421,7 @@
 do_kill:
     if (low_ram_device) {
         /* For Go devices kill only one task */
-        if (find_and_kill_processes(level, level_oomadj[level], 0) == 0) {
+        if (find_and_kill_processes(level_oomadj[level], 0) == 0) {
             if (debug_process_killing) {
                 ALOGI("Nothing to kill");
             }
@@ -1389,6 +1430,8 @@
         }
     } else {
         int pages_freed;
+        static struct timespec last_report_tm;
+        static unsigned long report_skip_count = 0;
 
         if (!use_minfree_levels) {
             /* If pressure level is less than critical and enough free swap then ignore */
@@ -1416,27 +1459,41 @@
             min_score_adj = level_oomadj[level];
         }
 
-        pages_freed = find_and_kill_processes(level, min_score_adj, pages_to_free);
+        pages_freed = find_and_kill_processes(min_score_adj, pages_to_free);
+
+        if (pages_freed == 0) {
+            /* Rate limit kill reports when nothing was reclaimed */
+            if (get_time_diff_ms(&last_report_tm, &curr_tm) < FAIL_REPORT_RLIMIT_MS) {
+                report_skip_count++;
+                return;
+            }
+        }
+
+        /* Log meminfo whenever we kill or when report rate limit allows */
+        meminfo_log(&mi);
+        if (pages_freed >= pages_to_free) {
+            /* Reset kill time only if reclaimed enough memory */
+            last_kill_tm = curr_tm;
+        }
 
         if (use_minfree_levels) {
-            ALOGI("Killing because cache %ldkB is below "
-                  "limit %ldkB for oom_adj %d\n"
-                  "   Free memory is %ldkB %s reserved",
-                  other_file * page_k, minfree * page_k, min_score_adj,
-                  other_free * page_k, other_free >= 0 ? "above" : "below");
+            ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB, cache(%ldkB) and "
+                "free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
+                pages_to_free * page_k, pages_freed * page_k,
+                other_file * page_k, mi.field.nr_free_pages * page_k,
+                zi.field.totalreserve_pages * page_k,
+                minfree * page_k, min_score_adj);
+        } else {
+            ALOGI("Killing to reclaim %ldkB, reclaimed %ldkB at oom_adj %d",
+                pages_to_free * page_k, pages_freed * page_k, min_score_adj);
         }
 
-        if (pages_freed < pages_to_free) {
-            ALOGI("Unable to free enough memory (pages to free=%d, pages freed=%d)",
-                  pages_to_free, pages_freed);
-        } else {
-            ALOGI("Reclaimed enough memory (pages to free=%d, pages freed=%d)",
-                  pages_to_free, pages_freed);
-            gettimeofday(&last_report_tm, NULL);
+        if (report_skip_count > 0) {
+            ALOGI("Suppressed %lu failed kill reports", report_skip_count);
+            report_skip_count = 0;
         }
-        if (pages_freed > 0) {
-            meminfo_log(&mi);
-        }
+
+        last_report_tm = curr_tm;
     }
 }
 
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
index 1996bae..f54b25c 100644
--- a/lmkd/tests/lmkd_test.cpp
+++ b/lmkd/tests/lmkd_test.cpp
@@ -39,7 +39,7 @@
 #define LMKDTEST_RESPAWN_FLAG "LMKDTEST_RESPAWN"
 
 #define LMKD_LOGCAT_MARKER "lowmemorykiller"
-#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Killing '%s'"
+#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Kill '%s'"
 #define OOM_MARKER "Out of memory"
 #define OOM_KILL_MARKER "Killed process"
 #define MIN_LOG_SIZE 100
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 0f56337..115b1a3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -16,6 +16,7 @@
 
 #include "logcat.h"
 
+#include <android-base/macros.h>
 #include <arpa/inet.h>
 #include <assert.h>
 #include <ctype.h>
@@ -41,6 +42,7 @@
 #include <atomic>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <android-base/file.h>
@@ -565,23 +567,14 @@
     return android_log_setPrintFormat(context->logformat, format);
 }
 
-static const char multipliers[][2] = { { "" }, { "K" }, { "M" }, { "G" } };
-
-static unsigned long value_of_size(unsigned long value) {
-    for (unsigned i = 0;
-         (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
-         value /= 1024, ++i)
-        ;
-    return value;
-}
-
-static const char* multiplier_of_size(unsigned long value) {
-    unsigned i;
+static std::pair<unsigned long, const char*> format_of_size(unsigned long value) {
+    static const char multipliers[][3] = {{""}, {"Ki"}, {"Mi"}, {"Gi"}};
+    size_t i;
     for (i = 0;
          (i < sizeof(multipliers) / sizeof(multipliers[0])) && (value >= 1024);
          value /= 1024, ++i)
         ;
-    return multipliers[i];
+    return std::make_pair(value, multipliers[i]);
 }
 
 // String to unsigned int, returns -1 if it fails
@@ -967,7 +960,7 @@
             case 't':
                 got_t = true;
                 mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
             case 'T':
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
                     char* cp = parseTime(tail_time, optarg);
@@ -1017,7 +1010,7 @@
                     getLogSize = true;
                     break;
                 }
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
 
             case 'G': {
                 char* cp;
@@ -1031,15 +1024,15 @@
                     case 'g':
                     case 'G':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case 'm':
                     case 'M':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case 'k':
                     case 'K':
                         setLogSize *= 1024;
-                    // FALLTHRU
+                        FALLTHROUGH_INTENDED;
                     case '\0':
                         break;
 
@@ -1059,7 +1052,7 @@
                     getPruneList = true;
                     break;
                 }
-            // FALLTHRU
+                FALLTHROUGH_INTENDED;
 
             case 'P':
                 setPruneList = optarg;
@@ -1472,12 +1465,14 @@
             if ((size < 0) || (readable < 0)) {
                 reportErrorName(&getSizeFail, dev->device, allSelected);
             } else {
+                auto size_format = format_of_size(size);
+                auto readable_format = format_of_size(readable);
                 std::string str = android::base::StringPrintf(
-                       "%s: ring buffer is %ld%sb (%ld%sb consumed),"
-                         " max entry is %db, max payload is %db\n",
+                       "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+                         " max entry is %d B, max payload is %d B\n",
                        dev->device,
-                       value_of_size(size), multiplier_of_size(size),
-                       value_of_size(readable), multiplier_of_size(readable),
+                       size_format.first, size_format.second,
+                       readable_format.first, readable_format.second,
                        (int)LOGGER_ENTRY_MAX_LEN,
                        (int)LOGGER_ENTRY_MAX_PAYLOAD);
                 TEMP_FAILURE_RETRY(write(context->output_fd,
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index cc1632a..9483bb2 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -31,6 +31,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 #include <log/event_tag_map.h>
@@ -557,47 +558,48 @@
 
     while (fgets(buffer, sizeof(buffer), fp)) {
         int size, consumed, max, payload;
-        char size_mult[3], consumed_mult[3];
+        char size_mult[4], consumed_mult[4];
         long full_size, full_consumed;
 
         size = consumed = max = payload = 0;
         // NB: crash log can be very small, not hit a Kb of consumed space
         //     doubly lucky we are not including it.
-        if (6 != sscanf(buffer,
-                        "%*s ring buffer is %d%2s (%d%2s consumed),"
-                        " max entry is %db, max payload is %db",
-                        &size, size_mult, &consumed, consumed_mult, &max,
-                        &payload)) {
-            fprintf(stderr, "WARNING: Parse error: %s", buffer);
-            continue;
-        }
+        EXPECT_EQ(6, sscanf(buffer,
+                            "%*s ring buffer is %d %3s (%d %3s consumed),"
+                            " max entry is %d B, max payload is %d B",
+                            &size, size_mult, &consumed, consumed_mult, &max, &payload))
+                << "Parse error on: " << buffer;
         full_size = size;
         switch (size_mult[0]) {
             case 'G':
                 full_size *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'M':
                 full_size *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'K':
                 full_size *= 1024;
-            /* FALLTHRU */
-            case 'b':
+                FALLTHROUGH_INTENDED;
+            case 'B':
                 break;
+            default:
+                ADD_FAILURE() << "Parse error on multiplier: " << size_mult;
         }
         full_consumed = consumed;
         switch (consumed_mult[0]) {
             case 'G':
                 full_consumed *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'M':
                 full_consumed *= 1024;
-            /* FALLTHRU */
+                FALLTHROUGH_INTENDED;
             case 'K':
                 full_consumed *= 1024;
-            /* FALLTHRU */
-            case 'b':
+                FALLTHROUGH_INTENDED;
+            case 'B':
                 break;
+            default:
+                ADD_FAILURE() << "Parse error on multiplier: " << consumed_mult;
         }
         EXPECT_GT((full_size * 9) / 4, full_consumed);
         EXPECT_GT(full_size, max);
@@ -1229,39 +1231,38 @@
         }
 
         int size, consumed, max, payload;
-        char size_mult[3], consumed_mult[3];
+        char size_mult[4], consumed_mult[4];
         size = consumed = max = payload = 0;
         if (6 == sscanf(buffer,
-                        "events: ring buffer is %d%2s (%d%2s consumed),"
-                        " max entry is %db, max payload is %db",
-                        &size, size_mult, &consumed, consumed_mult, &max,
-                        &payload)) {
+                        "events: ring buffer is %d %3s (%d %3s consumed),"
+                        " max entry is %d B, max payload is %d B",
+                        &size, size_mult, &consumed, consumed_mult, &max, &payload)) {
             long full_size = size, full_consumed = consumed;
 
             switch (size_mult[0]) {
                 case 'G':
                     full_size *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'M':
                     full_size *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'K':
                     full_size *= 1024;
-                /* FALLTHRU */
-                case 'b':
+                    FALLTHROUGH_INTENDED;
+                case 'B':
                     break;
             }
             switch (consumed_mult[0]) {
                 case 'G':
                     full_consumed *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'M':
                     full_consumed *= 1024;
-                /* FALLTHRU */
+                    FALLTHROUGH_INTENDED;
                 case 'K':
                     full_consumed *= 1024;
-                /* FALLTHRU */
-                case 'b':
+                    FALLTHROUGH_INTENDED;
+                case 'B':
                     break;
             }
             EXPECT_GT(full_size, full_consumed);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index e4393a3..513c0c3 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -475,9 +475,7 @@
 static int convertKernelPrioToAndroidPrio(int pri) {
     switch (pri & LOG_PRIMASK) {
         case LOG_EMERG:
-        // FALLTHRU
         case LOG_ALERT:
-        // FALLTHRU
         case LOG_CRIT:
             return ANDROID_LOG_FATAL;
 
@@ -488,9 +486,7 @@
             return ANDROID_LOG_WARN;
 
         default:
-        // FALLTHRU
         case LOG_NOTICE:
-        // FALLTHRU
         case LOG_INFO:
             break;
 
diff --git a/logd/main.cpp b/logd/main.cpp
index b697d44..8c38d9a 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -104,7 +104,8 @@
         return -1;
     }
 
-    if (__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
+    if (!__android_logger_property_get_bool("ro.debuggable",
+                                            BOOL_DEFAULT_FALSE) &&
         prctl(PR_SET_DUMPABLE, 0) == -1) {
         android::prdebug("failed to clear PR_SET_DUMPABLE");
         return -1;
diff --git a/mkbootimg/include/bootimg/bootimg.h b/mkbootimg/include/bootimg/bootimg.h
index bce308b..4432f9e 100644
--- a/mkbootimg/include/bootimg/bootimg.h
+++ b/mkbootimg/include/bootimg/bootimg.h
@@ -110,25 +110,25 @@
  */
 
 struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
-    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO image */
-    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo in boot image */
+    uint32_t recovery_dtbo_size;   /* size in bytes for recovery DTBO/ACPIO image */
+    uint64_t recovery_dtbo_offset; /* offset to recovery dtbo/acpio in boot image */
     uint32_t header_size;
 } __attribute__((packed));
 
 /* When the boot image header has a version of 1, the structure of the boot
  * image is as follows:
  *
- * +-----------------+
- * | boot header     | 1 page
- * +-----------------+
- * | kernel          | n pages
- * +-----------------+
- * | ramdisk         | m pages
- * +-----------------+
- * | second stage    | o pages
- * +-----------------+
- * | recovery dtbo   | p pages
- * +-----------------+
+ * +---------------------+
+ * | boot header         | 1 page
+ * +---------------------+
+ * | kernel              | n pages
+ * +---------------------+
+ * | ramdisk             | m pages
+ * +---------------------+
+ * | second stage        | o pages
+ * +---------------------+
+ * | recovery dtbo/acpio | p pages
+ * +---------------------+
  * n = (kernel_size + page_size - 1) / page_size
  * m = (ramdisk_size + page_size - 1) / page_size
  * o = (second_size + page_size - 1) / page_size
@@ -136,13 +136,14 @@
  *
  * 0. all entities are page_size aligned in flash
  * 1. kernel and ramdisk are required (size != 0)
- * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
+ * 2. recovery_dtbo/recovery_acpio is required for recovery.img in non-A/B
+ *    devices(recovery_dtbo_size != 0)
  * 3. second is optional (second_size == 0 -> no second)
  * 4. load each element (kernel, ramdisk, second) at
  *    the specified physical address (kernel_addr, etc)
- * 5. If booting to recovery mode in a non-A/B device, extract recovery dtbo and
- *    apply the correct set of overlays on the base device tree depending on the
- *    hardware/product revision.
+ * 5. If booting to recovery mode in a non-A/B device, extract recovery
+ *    dtbo/acpio and apply the correct set of overlays on the base device tree
+ *    depending on the hardware/product revision.
  * 6. prepare tags at tag_addr.  kernel_args[] is
  *    appended to the kernel commandline in the tags.
  * 7. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index fda9af0..2eb2bab 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -161,7 +161,10 @@
                         required=True)
     parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
     parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
-    parser.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+    recovery_dtbo_group = parser.add_mutually_exclusive_group()
+    recovery_dtbo_group.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
+    recovery_dtbo_group.add_argument('--recovery_acpio', help='path to the recovery ACPIO',
+                                     type=FileType('rb'), metavar='RECOVERY_ACPIO', dest='recovery_dtbo')
     parser.add_argument('--cmdline', help='extra arguments to be passed on the '
                         'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
     parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
diff --git a/platform_tools_tool_version.mk b/platform_tools_tool_version.mk
deleted file mode 100644
index eed2ab5..0000000
--- a/platform_tools_tool_version.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright (C) 2017 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# We rewrite ${PLATFORM_SDK_VERSION} with 0 rather than $(PLATFORM_SDK_VERSION)
-# because on the actual platform tools release branches the file contains a
-# literal instead. Using 0 lets us easily distinguish non-canonical builds.
-platform_tools_version := $(shell sed \
-    's/$${PLATFORM_SDK_VERSION}/0/ ; s/^Pkg.Revision=\(.*\)/\1/p ; d' \
-    development/sdk/plat_tools_source.prop_template \
-  )
-tool_version := $(platform_tools_version)-$(BUILD_NUMBER_FROM_FILE)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 2429b49..b68dc34 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -94,9 +94,9 @@
   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
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product_services
 else
-  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product-services $(TARGET_ROOT_OUT)/product-services
+  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product_services $(TARGET_ROOT_OUT)/product_services
 endif
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
@@ -206,6 +206,7 @@
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
 ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
+check_backward_compatibility := true
 vndk_version := $(PLATFORM_VNDK_VERSION)
 include $(LOCAL_PATH)/update_and_install_ld_config.mk
 
@@ -243,6 +244,7 @@
 LOCAL_MODULE_STEM := $$(LOCAL_MODULE)
 include $(BUILD_SYSTEM)/base_rules.mk
 ld_config_template := $(LOCAL_PATH)/etc/ld.config.txt
+check_backward_compatibility := true
 vndk_version := $(1)
 lib_list_from_prebuilts := true
 include $(LOCAL_PATH)/update_and_install_ld_config.mk
@@ -289,7 +291,7 @@
 LOCAL_MODULE := ld.config.recovery.txt
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := etc/ld.config.recovery.txt
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/etc
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc
 LOCAL_MODULE_STEM := ld.config.txt
 include $(BUILD_PREBUILT)
 
diff --git a/rootdir/etc/OWNERS b/rootdir/etc/OWNERS
new file mode 100644
index 0000000..8a65b23
--- /dev/null
+++ b/rootdir/etc/OWNERS
@@ -0,0 +1,4 @@
+danalbert@google.com
+enh@google.com
+jiyong@google.com
+rprichard@google.com
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 7d22a3a..d3e80c9 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -8,7 +8,6 @@
 dir.system = /system/bin/
 dir.system = /system/xbin/
 dir.system = /%PRODUCT%/bin/
-dir.system = /%PRODUCTSERVICES%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -41,7 +40,7 @@
 
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCTSERVICES%/${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
@@ -54,7 +53,7 @@
 namespace.default.permitted.paths += /system/${LIB}/extractors
 namespace.default.permitted.paths += /system/${LIB}/hw
 namespace.default.permitted.paths += /%PRODUCT%/${LIB}
-namespace.default.permitted.paths += /%PRODUCTSERVICES%/${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
@@ -69,18 +68,18 @@
 namespace.default.permitted.paths += /%PRODUCT%/framework
 namespace.default.permitted.paths += /%PRODUCT%/app
 namespace.default.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.permitted.paths += /%PRODUCTSERVICES%/framework
-namespace.default.permitted.paths += /%PRODUCTSERVICES%/app
-namespace.default.permitted.paths += /%PRODUCTSERVICES%/priv-app
+namespace.default.permitted.paths += /%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.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
-namespace.default.asan.search.paths += /data/asan/product-services/${LIB}
-namespace.default.asan.search.paths +=           /product-services/${LIB}
+namespace.default.asan.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
@@ -100,10 +99,10 @@
 namespace.default.asan.permitted.paths += /%PRODUCT%/framework
 namespace.default.asan.permitted.paths += /%PRODUCT%/app
 namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/${LIB}
-namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/framework
-namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/app
-namespace.default.asan.permitted.paths += /%PRODUCTSERVICES%/priv-app
+namespace.default.asan.permitted.paths += /%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
 
 ###############################################################################
@@ -340,12 +339,14 @@
 
 namespace.system.search.paths  = /system/${LIB}
 namespace.system.search.paths += /%PRODUCT%/${LIB}
-namespace.system.search.paths += /%PRODUCTSERVICES%/${LIB}
+namespace.system.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.system.asan.search.paths  = /data/asan/system/${LIB}
 namespace.system.asan.search.paths +=           /system/${LIB}
 namespace.system.asan.search.paths += /data/asan/product/${LIB}
-namespace.system.asan.search.paths +=           /product/${LIB}
+namespace.system.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.system.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 ###############################################################################
 # Namespace config for binaries under /postinstall.
@@ -359,4 +360,4 @@
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCTSERVICES%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index db65c14..7e354ac 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,7 +7,7 @@
 # absolute path of an executable is selected.
 dir.system = /system/bin/
 dir.system = /system/xbin/
-dir.system = /product/bin/
+dir.system = /%PRODUCT%/bin/
 
 dir.vendor = /odm/bin/
 dir.vendor = /vendor/bin/
@@ -41,7 +41,8 @@
 namespace.default.search.paths  = /system/${LIB}
 namespace.default.search.paths += /odm/${LIB}
 namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
@@ -50,7 +51,9 @@
 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 +=           /%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 ###############################################################################
 # "sphal" namespace
@@ -209,7 +212,8 @@
 namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
 namespace.default.search.paths += /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
 
 namespace.default.asan.search.paths  = /data/asan/odm/${LIB}
 namespace.default.asan.search.paths +=           /odm/${LIB}
@@ -230,7 +234,9 @@
 namespace.default.asan.search.paths += /data/asan/system/${LIB}
 namespace.default.asan.search.paths +=           /system/${LIB}
 namespace.default.asan.search.paths += /data/asan/product/${LIB}
-namespace.default.asan.search.paths +=           /product/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
+namespace.default.asan.search.paths +=           /%PRODUCT_SERVICES%/${LIB}
 
 ###############################################################################
 # Namespace config for binaries under /postinstall.
@@ -243,4 +249,5 @@
 [postinstall]
 namespace.default.isolated = false
 namespace.default.search.paths  = /system/${LIB}
-namespace.default.search.paths += /product/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
+namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index e20b95d..d8f6095 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,6 +1,7 @@
 # See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
+libbinder_ndk.so
 libc.so
 libcamera2ndk.so
 libdl.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index ff0813d..20905bf 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -2,6 +2,7 @@
 libandroid.so
 libandroidthings.so
 libaaudio.so
+libbinder_ndk.so
 libc.so
 libcamera2ndk.so
 libdl.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 3c46094..4ece5b5 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,6 +1,7 @@
 # See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
+libbinder_ndk.so
 libc.so
 libcamera2ndk.so
 libdl.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index f1e5525..f39ea7c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -49,6 +49,10 @@
     copy /proc/cmdline /dev/urandom
     copy /default.prop /dev/urandom
 
+    symlink /proc/self/fd/0 /dev/stdin
+    symlink /proc/self/fd/1 /dev/stdout
+    symlink /proc/self/fd/2 /dev/stderr
+
     symlink /system/bin /bin
     symlink /system/etc /etc
 
@@ -302,12 +306,12 @@
     # /data, which in turn can only be loaded when system properties are present.
     trigger post-fs-data
 
-    # Now we can start zygote for devices with file based encryption
-    trigger zygote-start
-
     # Load persist properties and override properties (if enabled) from /data.
     trigger load_persist_props_action
 
+    # Now we can start zygote for devices with file based encryption
+    trigger zygote-start
+
     # Remove a file to wake up anything waiting for firmware.
     trigger firmware_mounts_complete
 
@@ -715,12 +719,12 @@
     class_start main
 
 on property:vold.decrypt=trigger_restart_framework
-    stop surfaceflinger
-    start surfaceflinger
     # A/B update verifier that marks a successful boot.
     exec_start update_verifier
     class_start main
     class_start late_start
+    setprop service.bootanim.exit 0
+    start bootanim
 
 on property:vold.decrypt=trigger_shutdown_framework
     class_reset late_start
diff --git a/rootdir/ld_config_backward_compatibility_check.py b/rootdir/ld_config_backward_compatibility_check.py
new file mode 100755
index 0000000..1a27578
--- /dev/null
+++ b/rootdir/ld_config_backward_compatibility_check.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import glob
+import os.path
+import re
+import sys
+
+PREBUILTS_VNDK_DIR = "prebuilts/vndk"
+VENDOR_DIRECTORIES = ('/vendor', '/odm')
+
+def find_latest_vndk_snapshot_version():
+  """Returns latest vndk snapshot version in current source tree.
+  It will skip the test if the snapshot directories are not found.
+
+  Returns:
+    latest_version: string
+  """
+  vndk_dir_list = glob.glob(PREBUILTS_VNDK_DIR + "/v*")
+  if not vndk_dir_list:
+    """Exit without error because we may have source trees that do not include
+    VNDK snapshot directories in it.
+    """
+    sys.exit(0)
+  vndk_ver_list = [re.match(r".*/v(\d+)", vndk_dir).group(1)
+                                          for vndk_dir in vndk_dir_list]
+  latest_version = max(vndk_ver_list)
+  if latest_version == '27':
+    """Exit without error because VNDK v27 is not using ld.config.txt template
+    """
+    sys.exit(0)
+  return latest_version
+
+def get_vendor_configuration(ld_config_file):
+  """Reads the ld.config.txt file to parse the namespace configurations.
+  It finds the configurations that include vendor directories.
+
+  Args:
+    ld_config_file: string, path (relative to build top) of the ld.config.txt
+                    file.
+  Returns:
+    configs: dict{string:[string]}, dictionary of namespace configurations.
+             it has 'section + property' names as keys and the directory list
+             as values.
+  """
+  try:
+    conf_file = open(ld_config_file)
+  except IOError:
+    print("error: could not read %s" % ld_config_file)
+    sys.exit(1)
+
+  configs = dict()
+  current_section = None
+
+  with conf_file:
+    for line in conf_file:
+      # ignore comments
+      found = line.find('#')
+      if found != -1:
+        line = line[:found]
+      line = line.strip()
+      if not line:
+        continue
+
+      if line[0] == '[' and line[-1] == ']':
+        # new section started
+        current_section = line[1:-1]
+        continue
+
+      if current_section == None:
+        continue
+
+      found = line.find('+=')
+      opr_len = 2
+      if found == -1:
+        found = line.find('=')
+        opr_len = 1
+      if found == -1:
+        continue
+
+      namespace = line[:found].strip()
+      if not namespace.endswith(".paths"):
+        # check ".paths" only
+        continue
+      namespace = '[' + current_section + ']' + namespace
+      values = line[found + opr_len:].strip()
+      directories = values.split(':')
+
+      for directory in directories:
+        if any(vendor_dir in directory for vendor_dir in VENDOR_DIRECTORIES):
+          if namespace in configs:
+            configs[namespace].append(directory)
+          else:
+            configs[namespace] = [directory]
+
+  return configs
+
+def get_snapshot_config(version):
+  """Finds the ld.config.{version}.txt file from the VNDK snapshot directory.
+  In the vndk prebuilt directory (prebuilts/vndk/v{version}), it searches
+  {arch}/configs/ld.config.{version}.txt file, where {arch} is one of ('arm64',
+  'arm', 'x86_64', 'x86').
+
+  Args:
+    version: string, the VNDK snapshot version to search.
+  Returns:
+    ld_config_file: string, relative path to ld.config.{version}.txt
+  """
+  arch_list = ('arm64', 'arm', 'x86_64', 'x86')
+  for arch in arch_list:
+    ld_config_file = (PREBUILTS_VNDK_DIR
+                + "/v{0}/{1}/configs/ld.config.{0}.txt".format(version, arch))
+    if os.path.isfile(ld_config_file):
+      return ld_config_file
+  print("error: cannot find ld.config.{0}.txt file in snapshot v{0}"
+                                                        .format(version))
+  sys.exit(1)
+
+def check_backward_compatibility(ld_config, vndk_snapshot_version):
+  """Checks backward compatibility for current ld.config.txt file with the
+  old ld.config.txt file. If any of the vendor directories in the old namespace
+  configurations are missing, the test will fail. It is allowed to have new
+  vendor directories in current ld.config.txt file.
+
+  Args:
+    ld_config: string, relative path to current ld.config.txt file.
+    vndk_snapshot_version: string, the VNDK snapshot version that has an old
+                           ld.config.txt file to compare.
+  Returns:
+    result: bool, True if the current configuration is backward compatible.
+  """
+  current_config = get_vendor_configuration(ld_config)
+  old_config = get_vendor_configuration(
+                                get_snapshot_config(vndk_snapshot_version))
+  for namespace in old_config:
+    if namespace not in current_config:
+      print("error: cannot find %s which was provided in ld.config.%s.txt"
+                                        % (namespace, vndk_snapshot_version))
+      return False
+    for path in old_config[namespace]:
+      if not path in current_config[namespace]:
+        print("error: %s for %s in ld.config.%s.txt are missing in %s"
+                % (path, namespace, vndk_snapshot_version, ld_config))
+        return False
+  return True
+
+def main():
+  if len(sys.argv) != 2:
+    print ("Usage: %s target_ld_config_txt_file_name" % sys.argv[0])
+    sys.exit(1)
+
+  latest_vndk_snapshot_version = find_latest_vndk_snapshot_version()
+  if not check_backward_compatibility(sys.argv[1],
+                                          latest_vndk_snapshot_version):
+    print("error: %s has backward incompatible changes to old "
+          "vendor partition." % sys.argv[1])
+    sys.exit(1)
+
+  # Current ld.config.txt file is backward compatible
+  sys.exit(0)
+
+if __name__ == '__main__':
+  main()
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 1b42c32..56a30b2 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -18,10 +18,13 @@
 
 # Read inputs
 ld_config_template := $(strip $(ld_config_template))
+check_backward_compatibility := $(strip $(check_backward_compatibility))
 vndk_version := $(strip $(vndk_version))
 lib_list_from_prebuilts := $(strip $(lib_list_from_prebuilts))
 libz_is_llndk := $(strip $(libz_is_llndk))
 
+compatibility_check_script := \
+  $(LOCAL_PATH)/ld_config_backward_compatibility_check.py
 intermediates_dir := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE))
 library_lists_dir := $(intermediates_dir)
 ifeq ($(lib_list_from_prebuilts),true)
@@ -82,11 +85,19 @@
 $(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries)
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_VERSION_SUFFIX := $(vndk_version_suffix)
 $(LOCAL_BUILT_MODULE): PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
+$(LOCAL_BUILT_MODULE): PRIVATE_COMP_CHECK_SCRIPT := $(compatibility_check_script)
 deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
   $(vndkprivate_libraries_file)
+ifeq ($(check_backward_compatibility),true)
+deps += $(compatibility_check_script)
+endif
 
 $(LOCAL_BUILT_MODULE): $(ld_config_template) $(deps)
 	@echo "Generate: $< -> $@"
+ifeq ($(check_backward_compatibility),true)
+	@echo "Checking backward compatibility..."
+	$(hide) $(PRIVATE_COMP_CHECK_SCRIPT) $<
+endif
 	@mkdir -p $(dir $@)
 	$(call private-filter-out-private-libs,$(PRIVATE_LLNDK_LIBRARIES_FILE),$(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)
 	$(hide) sed -e "s?%LLNDK_LIBRARIES%?$$(cat $(PRIVATE_INTERMEDIATES_DIR)/llndk_filtered)?g" $< >$@
@@ -104,13 +115,15 @@
 	$(hide) sed -i.bak -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $@
 	$(hide) sed -i.bak -e 's?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g' $@
 	$(hide) sed -i.bak -e 's?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g' $@
-	$(hide) sed -i.bak -e 's?%PRODUCTSERVICES%?$(TARGET_COPY_OUT_PRODUCTSERVICES)?g' $@
+	$(hide) sed -i.bak -e 's?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g' $@
 	$(hide) rm -f $@.bak
 
 ld_config_template :=
+check_backward_compatibility :=
 vndk_version :=
 lib_list_from_prebuilts :=
 libz_is_llndk :=
+compatibility_check_script :=
 intermediates_dir :=
 library_lists_dir :=
 llndk_libraries_file :=
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 3d7521c..2d4a26f 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -21,6 +21,7 @@
         "tcpdump",
         "toolbox",
         "toybox",
+        "unzip",
     ],
 }
 
@@ -31,6 +32,7 @@
         "sh.recovery",
         "toolbox.recovery",
         "toybox.recovery",
+        "unzip.recovery",
     ],
 }
 
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 3a718fa..fffb3d2 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -68,31 +68,33 @@
 
 struct uid_record {
     string name;
-    struct uid_io_usage ios;
+    uid_io_usage ios;
 };
 
 struct uid_records {
     uint64_t start_ts;
-    vector<struct uid_record> entries;
+    vector<uid_record> entries;
 };
 
 class uid_monitor {
 private:
     FRIEND_TEST(storaged_test, uid_monitor);
+    FRIEND_TEST(storaged_test, load_uid_io_proto);
+
     // last dump from /proc/uid_io/stats, uid -> uid_info
-    unordered_map<uint32_t, uid_info> last_uid_io_stats;
+    unordered_map<uint32_t, uid_info> last_uid_io_stats_;
     // current io usage for next report, app name -> uid_io_usage
-    unordered_map<string, struct uid_io_usage> curr_io_stats;
+    unordered_map<string, uid_io_usage> curr_io_stats_;
     // io usage records, end timestamp -> {start timestamp, vector of records}
-    map<uint64_t, struct uid_records> io_history;
+    map<uint64_t, uid_records> io_history_;
     // charger ON/OFF
-    charger_stat_t charger_stat;
+    charger_stat_t charger_stat_;
     // protects curr_io_stats, last_uid_io_stats, records and charger_stat
-    Mutex uidm_mutex;
+    Mutex uidm_mutex_;
     // start time for IO records
-    uint64_t start_ts;
+    uint64_t start_ts_;
     // true if UID_IO_STATS_PATH is accessible
-    const bool enable;
+    const bool enabled_;
 
     // reads from /proc/uid_io/stats
     unordered_map<uint32_t, uid_info> get_uid_io_stats_locked();
@@ -103,6 +105,10 @@
     // writes io_history to protobuf
     void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);
 
+    // Ensure that io_history_ can append |n| items without exceeding
+    // MAX_UID_RECORDS_SIZE in size.
+    void maybe_shrink_history_for_items(size_t nitems);
+
 public:
     uid_monitor();
     // called by storaged main thread
@@ -110,16 +116,20 @@
     // called by storaged -u
     unordered_map<uint32_t, uid_info> get_uid_io_stats();
     // called by dumpsys
-    map<uint64_t, struct uid_records> dump(
+    map<uint64_t, uid_records> dump(
         double hours, uint64_t threshold, bool force_report);
     // called by battery properties listener
     void set_charger_state(charger_stat_t stat);
     // called by storaged periodic_chore or dump with force_report
-    bool enabled() { return enable; };
+    bool enabled() { return enabled_; };
     void report(unordered_map<int, StoragedProto>* protos);
     // restores io_history from protobuf
-    void load_uid_io_proto(const UidIOUsage& proto);
+    void load_uid_io_proto(userid_t user_id, const UidIOUsage& proto);
     void clear_user_history(userid_t user_id);
+
+    map<uint64_t, uid_records>& io_history() { return io_history_; }
+
+    static constexpr int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
 };
 
 #endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index f346c38..77c6167 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -194,7 +194,7 @@
         return;
     }
 
-    mUidm.load_uid_io_proto(proto.uid_io_usage());
+    mUidm.load_uid_io_proto(user_id, proto.uid_io_usage());
 
     if (user_id == USER_SYSTEM) {
         storage_info->load_perf_history_proto(proto.perf_history());
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 1050033..8b5001d 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -129,6 +129,10 @@
 
     bool success = false;
     auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
+        if (result == Result::NOT_SUPPORTED) {
+            LOG_TO(SYSTEM, DEBUG) << "getDiskStats is not supported on health HAL.";
+            return;
+        }
         if (result != Result::SUCCESS || halStats.size() == 0) {
             LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
                                   << " and size " << halStats.size();
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 5605f66..8c0b3d1 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -370,8 +370,12 @@
 
 void health_storage_info_t::report() {
     auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
+        if (result == Result::NOT_SUPPORTED) {
+            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo is not supported on health HAL.";
+            return;
+        }
         if (result != Result::SUCCESS || halInfos.size() == 0) {
-            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
+            LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with result " << toString(result)
                                   << " and size " << halInfos.size();
             return;
         }
@@ -380,7 +384,7 @@
     });
 
     if (!ret.isOk()) {
-        LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
+        LOG_TO(SYSTEM, ERROR) << "getStorageInfo failed with " << ret.description();
     }
 }
 
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index 5745782..55380ba 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -21,6 +21,7 @@
 
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <android/content/pm/IPackageManagerNative.h>
 #include <android-base/file.h>
@@ -50,7 +51,7 @@
 
 std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats()
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
     return get_uid_io_stats_locked();
 };
 
@@ -178,10 +179,10 @@
             uid_io_stats[u.uid].name = std::to_string(u.uid);
             uids.push_back(u.uid);
             uid_names.push_back(&uid_io_stats[u.uid].name);
-            if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
+            if (last_uid_io_stats_.find(u.uid) == last_uid_io_stats_.end()) {
                 refresh_uid_names = true;
             } else {
-                uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+                uid_io_stats[u.uid].name = last_uid_io_stats_[u.uid].name;
             }
         } else {
             task_info t;
@@ -200,8 +201,6 @@
 
 namespace {
 
-const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
-
 inline size_t history_size(
     const std::map<uint64_t, struct uid_records>& history)
 {
@@ -218,12 +217,12 @@
 {
     // remove records more than 5 days old
     if (curr_ts > 5 * DAY_TO_SEC) {
-        auto it = io_history.lower_bound(curr_ts - 5 * DAY_TO_SEC);
-        io_history.erase(io_history.begin(), it);
+        auto it = io_history_.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+        io_history_.erase(io_history_.begin(), it);
     }
 
     struct uid_records new_records;
-    for (const auto& p : curr_io_stats) {
+    for (const auto& p : curr_io_stats_) {
         struct uid_record record = {};
         record.name = p.first;
         if (!p.second.uid_ios.is_zero()) {
@@ -237,23 +236,26 @@
         }
     }
 
-    curr_io_stats.clear();
-    new_records.start_ts = start_ts;
-    start_ts = curr_ts;
+    curr_io_stats_.clear();
+    new_records.start_ts = start_ts_;
+    start_ts_ = curr_ts;
 
     if (new_records.entries.empty())
       return;
 
     // make some room for new records
-    ssize_t overflow = history_size(io_history) +
-        new_records.entries.size() - MAX_UID_RECORDS_SIZE;
-    while (overflow > 0 && io_history.size() > 0) {
-        auto del_it = io_history.begin();
-        overflow -= del_it->second.entries.size();
-        io_history.erase(io_history.begin());
-    }
+    maybe_shrink_history_for_items(new_records.entries.size());
 
-    io_history[curr_ts] = new_records;
+    io_history_[curr_ts] = new_records;
+}
+
+void uid_monitor::maybe_shrink_history_for_items(size_t nitems) {
+    ssize_t overflow = history_size(io_history_) + nitems - MAX_UID_RECORDS_SIZE;
+    while (overflow > 0 && io_history_.size() > 0) {
+        auto del_it = io_history_.begin();
+        overflow -= del_it->second.entries.size();
+        io_history_.erase(io_history_.begin());
+    }
 }
 
 std::map<uint64_t, struct uid_records> uid_monitor::dump(
@@ -263,7 +265,7 @@
         report(nullptr);
     }
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     std::map<uint64_t, struct uid_records> dump_records;
     uint64_t first_ts = 0;
@@ -272,7 +274,7 @@
         first_ts = time(NULL) - hours * HOUR_TO_SEC;
     }
 
-    for (auto it = io_history.lower_bound(first_ts); it != io_history.end(); ++it) {
+    for (auto it = io_history_.lower_bound(first_ts); it != io_history_.end(); ++it) {
         const std::vector<struct uid_record>& recs = it->second.entries;
         struct uid_records filtered;
 
@@ -311,29 +313,29 @@
 
     for (const auto& it : uid_io_stats) {
         const uid_info& uid = it.second;
-        if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
-            curr_io_stats[uid.name] = {};
+        if (curr_io_stats_.find(uid.name) == curr_io_stats_.end()) {
+            curr_io_stats_[uid.name] = {};
         }
 
-        struct uid_io_usage& usage = curr_io_stats[uid.name];
+        struct uid_io_usage& usage = curr_io_stats_[uid.name];
         usage.user_id = multiuser_get_user_id(uid.uid);
 
         int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
-            last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
+            last_uid_io_stats_[uid.uid].io[FOREGROUND].read_bytes;
         int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
-            last_uid_io_stats[uid.uid].io[BACKGROUND].read_bytes;
+            last_uid_io_stats_[uid.uid].io[BACKGROUND].read_bytes;
         int64_t fg_wr_delta = uid.io[FOREGROUND].write_bytes -
-            last_uid_io_stats[uid.uid].io[FOREGROUND].write_bytes;
+            last_uid_io_stats_[uid.uid].io[FOREGROUND].write_bytes;
         int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
-            last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
+            last_uid_io_stats_[uid.uid].io[BACKGROUND].write_bytes;
 
-        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat_] +=
             (fg_rd_delta < 0) ? 0 : fg_rd_delta;
-        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat_] +=
             (bg_rd_delta < 0) ? 0 : bg_rd_delta;
-        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat_] +=
             (fg_wr_delta < 0) ? 0 : fg_wr_delta;
-        usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat_] +=
             (bg_wr_delta < 0) ? 0 : bg_wr_delta;
 
         for (const auto& task_it : uid.tasks) {
@@ -341,34 +343,34 @@
             const pid_t pid = task_it.first;
             const std::string& comm = task_it.second.comm;
             int64_t task_fg_rd_delta = task.io[FOREGROUND].read_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;
             int64_t task_bg_rd_delta = task.io[BACKGROUND].read_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;
             int64_t task_fg_wr_delta = task.io[FOREGROUND].write_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;
             int64_t task_bg_wr_delta = task.io[BACKGROUND].write_bytes -
-                last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
+                last_uid_io_stats_[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
 
             io_usage& task_usage = usage.task_ios[comm];
-            task_usage.bytes[READ][FOREGROUND][charger_stat] +=
+            task_usage.bytes[READ][FOREGROUND][charger_stat_] +=
                 (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;
-            task_usage.bytes[READ][BACKGROUND][charger_stat] +=
+            task_usage.bytes[READ][BACKGROUND][charger_stat_] +=
                 (task_bg_rd_delta < 0) ? 0 : task_bg_rd_delta;
-            task_usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+            task_usage.bytes[WRITE][FOREGROUND][charger_stat_] +=
                 (task_fg_wr_delta < 0) ? 0 : task_fg_wr_delta;
-            task_usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+            task_usage.bytes[WRITE][BACKGROUND][charger_stat_] +=
                 (task_bg_wr_delta < 0) ? 0 : task_bg_wr_delta;
         }
     }
 
-    last_uid_io_stats = uid_io_stats;
+    last_uid_io_stats_ = uid_io_stats;
 }
 
 void uid_monitor::report(unordered_map<int, StoragedProto>* protos)
 {
     if (!enabled()) return;
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     update_curr_io_stats_locked();
     add_records_locked(time(NULL));
@@ -408,7 +410,7 @@
 
 void uid_monitor::update_uid_io_proto(unordered_map<int, StoragedProto>* protos)
 {
-    for (const auto& item : io_history) {
+    for (const auto& item : io_history_) {
         const uint64_t& end_ts = item.first;
         const struct uid_records& recs = item.second;
         unordered_map<userid_t, UidIOItem*> user_items;
@@ -448,9 +450,9 @@
 
 void uid_monitor::clear_user_history(userid_t user_id)
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
-    for (auto& item : io_history) {
+    for (auto& item : io_history_) {
         vector<uid_record>* entries = &item.second.entries;
         entries->erase(
             remove_if(entries->begin(), entries->end(),
@@ -459,27 +461,42 @@
             entries->end());
     }
 
-    for (auto it = io_history.begin(); it != io_history.end(); ) {
+    for (auto it = io_history_.begin(); it != io_history_.end(); ) {
         if (it->second.entries.empty()) {
-            it = io_history.erase(it);
+            it = io_history_.erase(it);
         } else {
             it++;
         }
     }
 }
 
-void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto)
+void uid_monitor::load_uid_io_proto(userid_t user_id, const UidIOUsage& uid_io_proto)
 {
     if (!enabled()) return;
 
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
     for (const auto& item_proto : uid_io_proto.uid_io_items()) {
         const UidIORecords& records_proto = item_proto.records();
-        struct uid_records* recs = &io_history[item_proto.end_ts()];
+        struct uid_records* recs = &io_history_[item_proto.end_ts()];
+
+        // It's possible that the same uid_io_proto file gets loaded more than
+        // once, for example, if system_server crashes. In this case we avoid
+        // adding duplicate entries, so we build a quick way to check for
+        // duplicates.
+        std::unordered_set<std::string> existing_uids;
+        for (const auto& rec : recs->entries) {
+            if (rec.ios.user_id == user_id) {
+                existing_uids.emplace(rec.name);
+            }
+        }
 
         recs->start_ts = records_proto.start_ts();
         for (const auto& rec_proto : records_proto.entries()) {
+            if (existing_uids.find(rec_proto.uid_name()) != existing_uids.end()) {
+                continue;
+            }
+
             struct uid_record record;
             record.name = rec_proto.uid_name();
             record.ios.user_id = rec_proto.user_id();
@@ -492,29 +509,35 @@
             }
             recs->entries.push_back(record);
         }
+
+        // We already added items, so this will just cull down to the maximum
+        // length. We do not remove anything if there is only one entry.
+        if (io_history_.size() > 1) {
+            maybe_shrink_history_for_items(0);
+        }
     }
 }
 
 void uid_monitor::set_charger_state(charger_stat_t stat)
 {
-    Mutex::Autolock _l(uidm_mutex);
+    Mutex::Autolock _l(uidm_mutex_);
 
-    if (charger_stat == stat) {
+    if (charger_stat_ == stat) {
         return;
     }
 
     update_curr_io_stats_locked();
-    charger_stat = stat;
+    charger_stat_ = stat;
 }
 
 void uid_monitor::init(charger_stat_t stat)
 {
-    charger_stat = stat;
+    charger_stat_ = stat;
 
-    start_ts = time(NULL);
-    last_uid_io_stats = get_uid_io_stats();
+    start_ts_ = time(NULL);
+    last_uid_io_stats_ = get_uid_io_stats();
 }
 
 uid_monitor::uid_monitor()
-    : enable(!access(UID_IO_STATS_PATH, R_OK)) {
+    : enabled_(!access(UID_IO_STATS_PATH, R_OK)) {
 }
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index ec47b65..64009c2 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -443,8 +443,9 @@
 
 TEST(storaged_test, uid_monitor) {
     uid_monitor uidm;
+    auto& io_history = uidm.io_history();
 
-    uidm.io_history[200] = {
+    io_history[200] = {
         .start_ts = 100,
         .entries = {
             { "app1", {
@@ -466,7 +467,7 @@
         },
     };
 
-    uidm.io_history[300] = {
+    io_history[300] = {
         .start_ts = 200,
         .entries = {
             { "app1", {
@@ -526,9 +527,9 @@
     EXPECT_EQ(user_1_item_1.records().entries(0).user_id(), 1UL);
     EXPECT_EQ(user_1_item_1.records().entries(0).uid_io().wr_fg_chg_off(), 1000UL);
 
-    uidm.io_history.clear();
+    io_history.clear();
 
-    uidm.io_history[300] = {
+    io_history[300] = {
         .start_ts = 200,
         .entries = {
             { "app1", {
@@ -539,7 +540,7 @@
         },
     };
 
-    uidm.io_history[400] = {
+    io_history[400] = {
         .start_ts = 300,
         .entries = {
             { "app1", {
@@ -550,16 +551,16 @@
         },
     };
 
-    uidm.load_uid_io_proto(protos[0].uid_io_usage());
-    uidm.load_uid_io_proto(protos[1].uid_io_usage());
+    uidm.load_uid_io_proto(0, protos[0].uid_io_usage());
+    uidm.load_uid_io_proto(1, protos[1].uid_io_usage());
 
-    EXPECT_EQ(uidm.io_history.size(), 3UL);
-    EXPECT_EQ(uidm.io_history.count(200), 1UL);
-    EXPECT_EQ(uidm.io_history.count(300), 1UL);
-    EXPECT_EQ(uidm.io_history.count(400), 1UL);
+    EXPECT_EQ(io_history.size(), 3UL);
+    EXPECT_EQ(io_history.count(200), 1UL);
+    EXPECT_EQ(io_history.count(300), 1UL);
+    EXPECT_EQ(io_history.count(400), 1UL);
 
-    EXPECT_EQ(uidm.io_history[200].start_ts, 100UL);
-    const vector<struct uid_record>& entries_0 = uidm.io_history[200].entries;
+    EXPECT_EQ(io_history[200].start_ts, 100UL);
+    const vector<struct uid_record>& entries_0 = io_history[200].entries;
     EXPECT_EQ(entries_0.size(), 3UL);
     EXPECT_EQ(entries_0[0].name, "app1");
     EXPECT_EQ(entries_0[0].ios.user_id, 0UL);
@@ -572,8 +573,8 @@
     EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
     EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
 
-    EXPECT_EQ(uidm.io_history[300].start_ts, 200UL);
-    const vector<struct uid_record>& entries_1 = uidm.io_history[300].entries;
+    EXPECT_EQ(io_history[300].start_ts, 200UL);
+    const vector<struct uid_record>& entries_1 = io_history[300].entries;
     EXPECT_EQ(entries_1.size(), 3UL);
     EXPECT_EQ(entries_1[0].name, "app1");
     EXPECT_EQ(entries_1[0].ios.user_id, 0UL);
@@ -585,8 +586,8 @@
     EXPECT_EQ(entries_1[2].ios.user_id, 1UL);
     EXPECT_EQ(entries_1[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
 
-    EXPECT_EQ(uidm.io_history[400].start_ts, 300UL);
-    const vector<struct uid_record>& entries_2 = uidm.io_history[400].entries;
+    EXPECT_EQ(io_history[400].start_ts, 300UL);
+    const vector<struct uid_record>& entries_2 = io_history[400].entries;
     EXPECT_EQ(entries_2.size(), 1UL);
     EXPECT_EQ(entries_2[0].name, "app1");
     EXPECT_EQ(entries_2[0].ios.user_id, 0UL);
@@ -615,14 +616,71 @@
 
     uidm.clear_user_history(0);
 
-    EXPECT_EQ(uidm.io_history.size(), 2UL);
-    EXPECT_EQ(uidm.io_history.count(200), 1UL);
-    EXPECT_EQ(uidm.io_history.count(300), 1UL);
+    EXPECT_EQ(io_history.size(), 2UL);
+    EXPECT_EQ(io_history.count(200), 1UL);
+    EXPECT_EQ(io_history.count(300), 1UL);
 
-    EXPECT_EQ(uidm.io_history[200].entries.size(), 1UL);
-    EXPECT_EQ(uidm.io_history[300].entries.size(), 1UL);
+    EXPECT_EQ(io_history[200].entries.size(), 1UL);
+    EXPECT_EQ(io_history[300].entries.size(), 1UL);
 
     uidm.clear_user_history(1);
 
-    EXPECT_EQ(uidm.io_history.size(), 0UL);
+    EXPECT_EQ(io_history.size(), 0UL);
+}
+
+TEST(storaged_test, load_uid_io_proto) {
+    uid_monitor uidm;
+    auto& io_history = uidm.io_history();
+
+    static const uint64_t kProtoTime = 200;
+    io_history[kProtoTime] = {
+        .start_ts = 100,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+            { "app2", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 2000,
+              }
+            },
+            { "app3", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 3000,
+              }
+            },
+        },
+    };
+
+    unordered_map<int, StoragedProto> protos;
+    uidm.update_uid_io_proto(&protos);
+    ASSERT_EQ(protos.size(), size_t(1));
+
+    // Loading the same proto many times should not add duplicate entries.
+    UidIOUsage user_0 = protos[0].uid_io_usage();
+    for (size_t i = 0; i < 10000; i++) {
+        uidm.load_uid_io_proto(0, user_0);
+    }
+    ASSERT_EQ(io_history.size(), size_t(1));
+    ASSERT_EQ(io_history[kProtoTime].entries.size(), size_t(3));
+
+    // Create duplicate entries until we go over the limit.
+    auto record = io_history[kProtoTime];
+    io_history.clear();
+    for (size_t i = 0; i < uid_monitor::MAX_UID_RECORDS_SIZE * 2; i++) {
+        if (i == kProtoTime) {
+            continue;
+        }
+        io_history[i] = record;
+    }
+    ASSERT_GT(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));
+
+    // After loading, the history should be truncated.
+    for (auto& item : *user_0.mutable_uid_io_items()) {
+        item.set_end_ts(io_history.size());
+    }
+    uidm.load_uid_io_proto(0, user_0);
+    ASSERT_LE(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));
 }
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e6def6b..39033ad 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -321,7 +321,7 @@
     char idstr[80];
     struct input_id id;
 
-    fd = open(device, O_RDWR);
+    fd = open(device, O_RDONLY | O_CLOEXEC);
     if(fd < 0) {
         if(print_flags & PRINT_DEVICE_ERRORS)
             fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
diff --git a/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc b/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
index e9d3054..503f3de 100644
--- a/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
+++ b/trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc
@@ -1,4 +1,4 @@
 service vendor.keymaster-3-0 /vendor/bin/hw/android.hardware.keymaster@3.0-service.trusty
     class early_hal
     user nobody
-    group system drmrpc
+    group drmrpc
diff --git a/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h b/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
index 5a80795..a483c0d 100644
--- a/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
+++ b/trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h
@@ -80,6 +80,8 @@
                              const keymaster_blob_t* input, const keymaster_blob_t* signature,
                              keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
     keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
+    keymaster_error_t delete_key(const keymaster_key_blob_t* key);
+    keymaster_error_t delete_all_keys();
 
   private:
     keymaster_error_t Send(uint32_t command, const Serializable& request,
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index 8c5cff6..0956fe6 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -167,7 +167,7 @@
         // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
         return translate_error(rc);
     } else {
-        ALOGE("Received %d byte response\n", rsp_size);
+        ALOGV("Received %d byte response\n", rsp_size);
     }
 
     const uint8_t* p = recv_buf;
diff --git a/trusty/keymaster/legacy/trusty_keymaster_device.cpp b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
index ea00a92..88c3e7b 100644
--- a/trusty/keymaster/legacy/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
@@ -70,8 +70,8 @@
     device_.export_key = export_key;
     device_.attest_key = attest_key;
     device_.upgrade_key = upgrade_key;
-    device_.delete_key = nullptr;
-    device_.delete_all_keys = nullptr;
+    device_.delete_key = delete_key;
+    device_.delete_all_keys = delete_all_keys;
     device_.begin = begin;
     device_.update = update;
     device_.finish = finish;
@@ -122,10 +122,10 @@
 void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
                          RequestType* request) {
     request->additional_params.Clear();
-    if (client_id) {
+    if (client_id && client_id->data_length > 0) {
         request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
     }
-    if (app_data) {
+    if (app_data && app_data->data_length > 0) {
         request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
     }
 }
@@ -606,6 +606,34 @@
     return trusty_keymaster_send(KM_ABORT_OPERATION, request, &response);
 }
 
+keymaster_error_t TrustyKeymasterDevice::delete_key(const keymaster_key_blob_t* key) {
+    ALOGD("Device received delete_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+
+    if (!key || !key->key_material)
+        return KM_ERROR_UNEXPECTED_NULL_POINTER;
+
+    DeleteKeyRequest request(message_version_);
+    request.SetKeyMaterial(*key);
+    DeleteKeyResponse response(message_version_);
+    return trusty_keymaster_send(KM_DELETE_KEY, request, &response);
+}
+
+keymaster_error_t TrustyKeymasterDevice::delete_all_keys() {
+    ALOGD("Device received delete_all_key");
+
+    if (error_ != KM_ERROR_OK) {
+        return error_;
+    }
+
+    DeleteAllKeysRequest request(message_version_);
+    DeleteAllKeysResponse response(message_version_);
+    return trusty_keymaster_send(KM_DELETE_ALL_KEYS, request, &response);
+}
+
 hw_device_t* TrustyKeymasterDevice::hw_device() {
     return &device_.common;
 }
@@ -719,4 +747,15 @@
     return convert_device(dev)->abort(operation_handle);
 }
 
+/* static */
+keymaster_error_t TrustyKeymasterDevice::delete_key(const keymaster2_device_t* dev,
+                                               const keymaster_key_blob_t* key) {
+   return convert_device(dev)->delete_key(key);
+}
+
+/* static */
+keymaster_error_t TrustyKeymasterDevice::delete_all_keys(const keymaster2_device_t* dev) {
+   return convert_device(dev)->delete_all_keys();
+}
+
 }  // namespace keymaster