Merge "adb: win32: set thread names"
diff --git a/adb/Android.bp b/adb/Android.bp
index 1469e77..6cff0be 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -174,12 +174,6 @@
         "libdiagnose_usb",
         "libmdnssd",
         "libusb",
-        "libandroidfw",
-        "libziparchive",
-        "libz",
-        "libutils",
-        "liblog",
-        "libcutils",
     ],
 }
 
@@ -262,12 +256,6 @@
         "liblog",
         "libmdnssd",
         "libusb",
-        "libandroidfw",
-        "libziparchive",
-        "libz",
-        "libutils",
-        "liblog",
-        "libcutils",
     ],
 
     stl: "libc++_static",
@@ -277,6 +265,10 @@
     // will violate ODR
     shared_libs: [],
 
+    required: [
+        "deploypatchgenerator",
+    ],
+
     target: {
         darwin: {
             cflags: [
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 791899e..62e8908 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -633,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;
 
@@ -827,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_install.cpp b/adb/client/adb_install.cpp
index 95574ed..c0d09fd 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -40,6 +40,8 @@
 #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;
@@ -130,7 +132,7 @@
 }
 
 static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
-                                bool use_localagent, const char* adb_path) {
+                                bool use_localagent) {
     printf("Performing Streamed Install\n");
 
     // The last argument must be the APK file
@@ -152,8 +154,7 @@
             printf("failed to extract metadata %d\n", metadata_len);
             return 1;
         } else {
-            int create_patch_result = create_patch(file, metadataTmpFile.path, patchTmpFile.path,
-                                                   use_localagent, adb_path);
+            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;
@@ -226,8 +227,8 @@
     }
 }
 
-static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy, bool use_localagent,
-                              const char* adb_path) {
+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;
@@ -269,8 +270,8 @@
             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, use_localagent, adb_path);
+            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;
@@ -380,14 +381,10 @@
         }
     }
 
-    std::string adb_path = android::base::GetExecutablePath();
-
-    if (adb_path.length() == 0) {
-        return 1;
-    }
     if (use_fastdeploy == true) {
-        bool agent_up_to_date =
-                update_agent(agent_update_strategy, use_localagent, adb_path.c_str());
+        fastdeploy_set_local_agent(use_localagent);
+
+        bool agent_up_to_date = update_agent(agent_update_strategy);
         if (agent_up_to_date == false) {
             printf("Failed to update agent, exiting\n");
             return 1;
@@ -397,10 +394,10 @@
     switch (installMode) {
         case INSTALL_PUSH:
             return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
-                                      use_fastdeploy, use_localagent, adb_path.c_str());
+                                      use_fastdeploy, use_localagent);
         case INSTALL_STREAM:
             return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
-                                        use_fastdeploy, use_localagent, adb_path.c_str());
+                                        use_fastdeploy, use_localagent);
         case INSTALL_DEFAULT:
         default:
             return 1;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index b55ae95..a5cfd7f 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -130,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"
@@ -354,8 +354,7 @@
 }
 
 void copy_to_file(int inFd, int outFd) {
-    constexpr size_t BUFSIZE = 32 * 1024;
-    std::vector<char> buf(BUFSIZE);
+    std::vector<char> buf(32 * 1024);
     int len;
     long total = 0;
     int old_stdin_mode = -1;
@@ -367,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");
@@ -1713,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) {
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index fda3889..2914836 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -14,26 +14,29 @@
  * limitations under the License.
  */
 
-#include <androidfw/ResourceTypes.h>
-#include <androidfw/ZipFileRO.h>
 #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"
 
-const long kRequiredAgentVersion = 0x00000001;
+static constexpr long kRequiredAgentVersion = 0x00000001;
 
-const char* kDeviceAgentPath = "/data/local/tmp/";
+static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+
+static bool use_localagent = false;
 
 long get_agent_version() {
     std::vector<char> versionOutputBuffer;
     std::vector<char> versionErrorBuffer;
 
-    int statusCode = capture_shell_command("/data/local/tmp/deployagent.sh version",
+    int statusCode = capture_shell_command("/data/local/tmp/deployagent version",
                                            &versionOutputBuffer, &versionErrorBuffer);
     long version = -1;
 
@@ -58,13 +61,15 @@
     return api_level;
 }
 
+void fastdeploy_set_local_agent(bool set_use_localagent) {
+    use_localagent = set_use_localagent;
+}
+
 // local_path - must start with a '/' and be relative to $ANDROID_PRODUCT_OUT
-static bool get_agent_component_host_path(bool use_localagent, const char* adb_path,
-                                          const char* local_path, const char* sdk_path,
+static bool get_agent_component_host_path(const char* local_path, const char* sdk_path,
                                           std::string* output_path) {
-    std::string mutable_adb_path = adb_path;
-    const char* adb_dir = dirname(&mutable_adb_path[0]);
-    if (adb_dir == nullptr) {
+    std::string adb_dir = android::base::GetExecutableDirectory();
+    if (adb_dir.empty()) {
         return false;
     }
 
@@ -76,26 +81,24 @@
         *output_path = android::base::StringPrintf("%s%s", product_out, local_path);
         return true;
     } else {
-        *output_path = android::base::StringPrintf("%s%s", adb_dir, sdk_path);
+        *output_path = adb_dir + sdk_path;
         return true;
     }
     return false;
 }
 
-static bool deploy_agent(bool checkTimeStamps, bool use_localagent, const char* adb_path) {
+static bool deploy_agent(bool checkTimeStamps) {
     std::vector<const char*> srcs;
-
     std::string agent_jar_path;
-    if (get_agent_component_host_path(use_localagent, adb_path, "/system/framework/deployagent.jar",
-                                      "/deployagent.jar", &agent_jar_path)) {
+    if (get_agent_component_host_path("/system/framework/deployagent.jar", "/deployagent.jar",
+                                      &agent_jar_path)) {
         srcs.push_back(agent_jar_path.c_str());
     } else {
         return false;
     }
 
     std::string agent_sh_path;
-    if (get_agent_component_host_path(use_localagent, adb_path, "/system/bin/deployagent.sh",
-                                      "/deployagent.sh", &agent_sh_path)) {
+    if (get_agent_component_host_path("/system/bin/deployagent", "/deployagent", &agent_sh_path)) {
         srcs.push_back(agent_sh_path.c_str());
     } else {
         return false;
@@ -104,7 +107,7 @@
     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.sh";
+        const char* kChmodCommandPattern = "chmod 777 %sdeployagent";
         std::string chmodCommand =
                 android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentPath);
         int ret = send_shell_command(chmodCommand);
@@ -114,17 +117,16 @@
     }
 }
 
-bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy, bool use_localagent,
-                  const char* adb_path) {
+bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy) {
     long agent_version = get_agent_version();
     switch (agentUpdateStrategy) {
         case FastDeploy_AgentUpdateAlways:
-            if (deploy_agent(false, use_localagent, adb_path) == false) {
+            if (deploy_agent(false) == false) {
                 return false;
             }
             break;
         case FastDeploy_AgentUpdateNewerTimeStamp:
-            if (deploy_agent(true, use_localagent, adb_path) == false) {
+            if (deploy_agent(true) == false) {
                 return false;
             }
             break;
@@ -136,7 +138,7 @@
                     printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
                            agent_version, kRequiredAgentVersion);
                 }
-                if (deploy_agent(false, use_localagent, adb_path) == false) {
+                if (deploy_agent(false) == false) {
                     return false;
                 }
             }
@@ -147,91 +149,54 @@
     return (agent_version == kRequiredAgentVersion);
 }
 
-static std::string get_string_from_utf16(const char16_t* input, int input_len) {
-    ssize_t utf8_length = utf16_to_utf8_length(input, input_len);
-    if (utf8_length <= 0) {
-        return {};
+static std::string get_aapt2_path() {
+    if (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 utf8;
-    utf8.resize(utf8_length);
-    utf16_to_utf8(input, input_len, &*utf8.begin(), utf8_length + 1);
-    return utf8;
+    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 bool get_packagename_from_apk(const char* apkPath, std::string* output) {
-    using namespace android;
+    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);
 
-    ZipFileRO* zipFile = ZipFileRO::open(apkPath);
-    if (zipFile == nullptr) {
-        return false;
+    if (system_capture(getPackagenameCommand.c_str(), *output) == 0) {
+        // strip any line end characters from the output
+        *output = android::base::Trim(*output);
+        return true;
     }
-
-    ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
-    if (entry == nullptr) {
-        return false;
-    }
-
-    uint32_t manifest_len = 0;
-    if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
-        return false;
-    }
-
-    std::vector<char> manifest_data(manifest_len);
-    if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
-        return false;
-    }
-
-    ResXMLTree tree;
-    status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
-    if (setto_status != NO_ERROR) {
-        return false;
-    }
-
-    ResXMLParser::event_code_t code;
-    while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT &&
-           code != ResXMLParser::END_DOCUMENT) {
-        switch (code) {
-            case ResXMLParser::START_TAG: {
-                size_t element_name_length;
-                const char16_t* element_name = tree.getElementName(&element_name_length);
-                if (element_name == nullptr) {
-                    continue;
-                }
-
-                std::u16string element_name_string(element_name, element_name_length);
-                if (element_name_string == u"manifest") {
-                    for (int i = 0; i < (int)tree.getAttributeCount(); i++) {
-                        size_t attribute_name_length;
-                        const char16_t* attribute_name_text =
-                                tree.getAttributeName(i, &attribute_name_length);
-                        if (attribute_name_text == nullptr) {
-                            continue;
-                        }
-                        std::u16string attribute_name_string(attribute_name_text,
-                                                             attribute_name_length);
-
-                        if (attribute_name_string == u"package") {
-                            size_t attribute_value_length;
-                            const char16_t* attribute_value_text =
-                                    tree.getAttributeStringValue(i, &attribute_value_length);
-                            if (attribute_value_text == nullptr) {
-                                continue;
-                            }
-                            *output = get_string_from_utf16(attribute_value_text,
-                                                            attribute_value_length);
-                            return true;
-                        }
-                    }
-                }
-                break;
-            }
-            default:
-                break;
-        }
-    }
-
     return false;
 }
 
@@ -241,7 +206,7 @@
         return -1;
     }
 
-    const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent.sh extract %s";
+    const char* kAgentExtractCommandPattern = "/data/local/tmp/deployagent extract %s";
     std::string extractCommand =
             android::base::StringPrintf(kAgentExtractCommandPattern, packageName.c_str());
 
@@ -257,43 +222,30 @@
     return ret;
 }
 
-// output is required to point to a valid output string (non-null)
-static bool patch_generator_command(bool use_localagent, const char* adb_path,
-                                    std::string* output) {
+static std::string get_patch_generator_command() {
     if (use_localagent) {
         // This should never happen on a Windows machine
-        const char* kGeneratorCommandPattern = "java -jar %s/framework/deploypatchgenerator.jar";
         const char* host_out = getenv("ANDROID_HOST_OUT");
         if (host_out == nullptr) {
-            return false;
+            fatal("Could not locate deploypatchgenerator.jar because $ANDROID_HOST_OUT is not "
+                  "defined");
         }
-        *output = android::base::StringPrintf(kGeneratorCommandPattern, host_out, host_out);
-        return true;
-    } else {
-        const char* kGeneratorCommandPattern = R"(java -jar "%s/deploypatchgenerator.jar")";
-        std::string mutable_adb_path = adb_path;
-        const char* adb_dir = dirname(&mutable_adb_path[0]);
-        if (adb_dir == nullptr) {
-            return false;
-        }
-
-        *output = android::base::StringPrintf(kGeneratorCommandPattern, adb_dir, adb_dir);
-        return true;
+        return android::base::StringPrintf("java -jar %s/framework/deploypatchgenerator.jar",
+                                           host_out);
     }
-    return false;
+
+    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,
-                 bool use_localagent, const char* adb_path) {
-    const char* kGeneratePatchCommandPattern = R"(%s "%s" "%s" > "%s")";
-    std::string patch_generator_command_string;
-    if (patch_generator_command(use_localagent, adb_path, &patch_generator_command_string) ==
-        false) {
-        return 1;
-    }
+int create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
     std::string generatePatchCommand = android::base::StringPrintf(
-            kGeneratePatchCommandPattern, patch_generator_command_string.c_str(), apkPath,
-            metadataPath, patchPath);
+            R"(%s "%s" "%s" > "%s")", get_patch_generator_command().c_str(), apkPath, metadataPath,
+            patchPath);
     return system(generatePatchCommand.c_str());
 }
 
@@ -302,19 +254,20 @@
     if (get_packagename_from_apk(apkPath, &packageName) == false) {
         return "";
     }
+
     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.sh apply %s %s -o %s";
+    const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -o %s";
 
     std::string packageName;
     if (get_packagename_from_apk(apkPath, &packageName) == false) {
         return -1;
     }
+
     std::string patchDevicePath = get_patch_path(apkPath);
 
     std::vector<const char*> srcs = {patchPath};
@@ -327,12 +280,12 @@
     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.sh apply %s %s -pm %s";
+    const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
 
     std::string packageName;
     if (get_packagename_from_apk(apkPath, &packageName) == false) {
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
index d8acd30..80f3875 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -18,20 +18,17 @@
 
 #include "adb.h"
 
-typedef enum EFastDeploy_AgentUpdateStrategy {
+enum FastDeploy_AgentUpdateStrategy {
     FastDeploy_AgentUpdateAlways,
     FastDeploy_AgentUpdateNewerTimeStamp,
     FastDeploy_AgentUpdateDifferentVersion
-} FastDeploy_AgentUpdateStrategy;
+};
 
-static constexpr int kFastDeployMinApi = 24;
-
+void fastdeploy_set_local_agent(bool use_localagent);
 int get_device_api_level();
-bool update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy, bool use_localagent,
-                  const char* adb_path);
+bool 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,
-                 bool use_localagent, const char* adb_path);
+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/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/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index ed5f944..76500d4 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -209,7 +209,7 @@
     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"};
+    std::vector<std::string> partitions{"/odm", "/oem", "/product_services", "/product", "/vendor"};
     if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
         partitions.push_back("/");
     } else {
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 30f4730..400b12f 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -14,24 +14,17 @@
 // limitations under the License.
 //
 
-java_library {
+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",
     }
 }
 
-cc_prebuilt_binary {
-    name: "deployagent.sh",
-
-    srcs: ["deployagent/deployagent.sh"],
-    required: ["deployagent"],
-    device_supported: true,
-}
-
 java_binary_host {
     name: "deploypatchgenerator",
     srcs: ["deploypatchgenerator/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 08fe029..be0bdd0 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -124,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)
 {
@@ -518,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/test_adb.py b/adb/test_adb.py
index cde2b22..7e73818 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -490,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/transport.cpp b/adb/transport.cpp
index 239403a..95df490 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -1040,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{
@@ -1059,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) {
@@ -1067,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());
 }
 
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 4fd483b..8353d89 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -187,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;
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/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/fastboot/constants.h b/fastboot/constants.h
index 063cd40..8a425ae 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -57,3 +57,4 @@
 #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 1e853bf..771c288 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -42,34 +42,80 @@
 using ::android::hardware::boot::V1_0::Slot;
 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_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) {
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 6ed6d32..55aca9c 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -137,3 +137,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..171e7ae 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -39,9 +39,10 @@
     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(); }
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index e3efbcb..a383c54 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -72,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;
     }
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..a960189 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -35,141 +35,230 @@
 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 GetPartitionIsLogical(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;
     }
     // 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..a44e729 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -21,19 +21,36 @@
 
 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 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 6a52b12..0ac57af 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -44,43 +44,10 @@
 #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;
 
-static constexpr char kStatusFormat[] = "%-50s ";
-
 void fb_init(fastboot::FastBootDriver& fbi) {
     fb = &fbi;
     auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
@@ -88,7 +55,9 @@
 }
 
 void fb_reinit(Transport* transport) {
-    fb->set_transport(transport);
+    if (Transport* old_transport = fb->set_transport(transport)) {
+        delete old_transport;
+    }
 }
 
 const std::string fb_get_error() {
@@ -99,81 +68,72 @@
     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, void* data, uint32_t sz) {
+    Status(StringPrintf("Sending '%s' (%u KB)", partition.c_str(), sz / 1024));
+    RUN_COMMAND(fb->Download(static_cast<char*>(data), 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_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) {
@@ -197,193 +157,108 @@
     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_require(const std::string& product, const std::string& var, bool invert, size_t count,
+                const char** values) {
+    Status("Checking '" + var + "'");
+
+    double start = now();
+
+    std::string var_value;
+    auto status = fb->GetVar(var, &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());
+        die("requirements not met!");
     }
 
-    if (!a.product.empty()) {
-        if (a.product != cur_product) {
+    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,
-                    a.product.c_str(), (split - a.start));
-            a.start = split;
-            return 0;
+                    product.c_str(), (split - start));
+            return;
         }
     }
 
-    int yes = match(resp, value, count);
+    int yes = match(var_value.c_str(), values, 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, "OKAY [%7.3fs]\n", (split - start));
+        return;
     }
 
     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, "Device %s is '%s'.\n", var.c_str(), var_value.c_str());
+    fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", values[0]);
+    for (size_t n = 1; n < count; n++) {
+        fprintf(stderr, " or '%s'", values[n]);
     }
     fprintf(stderr, ".\n\n");
-    return -1;
+    die("requirements not met!");
 }
 
-static int cb_require(Action& a, int status, const char* resp) {
-    return cb_check(a, status, resp, 0);
-}
+void fb_display(const std::string& label, const std::string& var) {
+    std::string value;
+    auto status = fb->GetVar(var, &value);
 
-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, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
+        return;
     }
-    fprintf(stderr, "%s: %s\n", static_cast<const char*>(a.data), resp);
-    free(static_cast<char*>(a.data));
-    return 0;
+    fprintf(stderr, "%s: %s\n", label.c_str(), value.c_str());
 }
 
-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;
-}
+void fb_query_save(const std::string& var, char* dest, uint32_t dest_size) {
+    std::string value;
+    auto status = fb->GetVar(var, &value);
 
-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;
+        fprintf(stderr, "getvar:%s FAILED (%s)\n", var.c_str(), fb->Error().c_str());
+        return;
     }
-    strncpy(reinterpret_cast<char*>(a.data), resp, a.size);
-    return 0;
+
+    strncpy(dest, value.c_str(), dest_size);
 }
 
-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, void* data, uint32_t size) {
+    Status("Downloading '" + name + "'");
+    RUN_COMMAND(fb->Download(static_cast<char*>(data), size));
 }
 
-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_queue_wait_for_disconnect() {
-    queue_action(OP_WAIT_FOR_DISCONNECT, "");
-}
-
-int64_t fb_execute_queue() {
-    int64_t status = 0;
-    for (auto& a : action_list) {
-        a->start = now();
-        if (!a->msg.empty()) {
-            fprintf(stderr, kStatusFormat, 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);
-        }
-    }
-    action_list.clear();
-    return status;
+void fb_wait_for_disconnect() {
+    fb->WaitForDisconnect();
 }
 
 bool fb_reboot_to_userspace() {
-    // First ensure that the queue is flushed.
-    fb_execute_queue();
-
-    fprintf(stderr, kStatusFormat, "Rebooting to userspace fastboot");
+    Status("Rebooting to userspace fastboot");
     verbose("\n");
 
     if (fb->RebootTo("fastboot") != fastboot::RetCode::SUCCESS) {
@@ -392,6 +267,6 @@
     }
     fprintf(stderr, "OKAY\n");
 
-    fb->set_transport(nullptr);
+    fb_reinit(nullptr);
     return true;
 }
diff --git a/fastboot/engine.h b/fastboot/engine.h
index f098ca7..d78cb13 100644
--- a/fastboot/engine.h
+++ b/fastboot/engine.h
@@ -44,36 +44,29 @@
 
 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, void* data, uint32_t sz);
+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_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
+                const char** values);
+void fb_display(const std::string& label, const std::string& var);
+void fb_query_save(const std::string& var, char* dest, uint32_t dest_size);
+void fb_reboot();
+void fb_command(const std::string& cmd, const std::string& msg);
+void fb_download(const std::string& name, void* data, uint32_t size);
+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();
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2887d3b..817afd0 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -688,7 +688,7 @@
         out[i] = xstrdup(strip(val[i]));
     }
 
-    fb_queue_require(product, var, invert, count, out);
+    fb_require(product, var, invert, count, out);
 }
 
 static void check_requirements(char* data, int64_t sz) {
@@ -702,15 +702,14 @@
             s++;
         }
     }
-    if (fb_execute_queue()) die("requirements not met!");
 }
 
-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 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) {
@@ -883,12 +882,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);
@@ -1145,7 +1144,7 @@
     for (const auto& [image, slot] : os_images_) {
         auto resize_partition = [](const std::string& partition) -> void {
             if (is_logical(partition)) {
-                fb_queue_resize_partition(partition, "0");
+                fb_resize_partition(partition, "0");
             }
         };
         do_for_partitions(image->part_name, slot, resize_partition, false);
@@ -1223,12 +1222,12 @@
         int64_t sz;
         void* data = source_.ReadFile(image.sig_name, &sz);
         if (data) {
-            fb_queue_download("signature", data, sz);
-            fb_queue_command("signature", "installing signature");
+            fb_download("signature", data, sz);
+            fb_command("signature", "installing signature");
         }
 
         if (is_logical(partition_name)) {
-            fb_queue_resize_partition(partition_name, std::to_string(buf->image_size));
+            fb_resize_partition(partition_name, std::to_string(buf->image_size));
         }
         flash_buf(partition_name.c_str(), buf);
     };
@@ -1247,17 +1246,13 @@
     if (!is_userspace_fastboot()) {
         reboot_to_userspace_fastboot();
     }
-    fb_queue_download_fd("super", fd, get_file_size(fd));
+    fb_download_fd("super", fd, get_file_size(fd));
 
     std::string command = "update-super:super";
     if (wipe_) {
         command += ":wipe";
     }
-    fb_queue_command(command, "Updating super partition");
-
-    // We need these commands to have finished before proceeding, since
-    // otherwise "getvar is-logical" may not return a correct answer below.
-    fb_execute_queue();
+    fb_command(command, "Updating super partition");
 }
 
 class ZipImageSource final : public ImageSource {
@@ -1279,9 +1274,9 @@
 }
 
 static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
-    queue_info_dump();
+    dump_info();
 
-    fb_queue_query_save("product", cur_product, sizeof(cur_product));
+    fb_query_save("product", cur_product, sizeof(cur_product));
 
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
@@ -1316,9 +1311,9 @@
 
 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));
+    fb_query_save("product", cur_product, sizeof(cur_product));
 
     FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe);
     tool.Flash();
@@ -1338,7 +1333,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) {
@@ -1641,7 +1636,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) {
@@ -1653,7 +1648,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")) {
@@ -1681,8 +1676,8 @@
             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");
+            fb_download("signature", data, sz);
+            fb_command("signature", "installing signature");
         } else if (command == "reboot") {
             wants_reboot = true;
 
@@ -1710,7 +1705,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;
@@ -1719,8 +1714,8 @@
             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");
+            fb_download("boot.img", data, sz);
+            fb_command("boot", "booting");
         } else if (command == "flash") {
             std::string pname = next_arg(&args);
 
@@ -1746,7 +1741,7 @@
 
             data = load_bootable_image(kernel, ramdisk, second_stage, &sz);
             auto flashraw = [&](const std::string& partition) {
-                fb_queue_flash(partition, data, sz);
+                fb_flash(partition, data, sz);
             };
             do_for_partitions(partition, slot_override, flashraw, true);
         } else if (command == "flashall") {
@@ -1778,10 +1773,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") {
@@ -1798,14 +1793,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());
         }
@@ -1817,7 +1812,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();
@@ -1832,22 +1827,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 ceee066..4a14131 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -58,7 +58,6 @@
 }
 
 FastBootDriver::~FastBootDriver() {
-    set_transport(nullptr);
 }
 
 RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
@@ -98,9 +97,9 @@
     return RawCommand("reboot-" + target, response, info);
 }
 
-RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
+RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,
                                   std::vector<std::string>* info) {
-    return RawCommand(Commands::SET_ACTIVE + part, response, 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,7 +140,7 @@
         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));
         }
     }
@@ -537,12 +536,9 @@
     return 0;
 }
 
-void FastBootDriver::set_transport(Transport* transport) {
-    if (transport_) {
-        transport_->Close();
-        delete transport_;
-    }
-    transport_ = transport;
+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 4647945..4d85ba0 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -89,7 +89,7 @@
     RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode RebootTo(std::string target, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
-    RetCode SetActive(const std::string& part, std::string* response = 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);
@@ -99,7 +99,7 @@
     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);
 
@@ -109,8 +109,8 @@
     std::string Error();
     RetCode WaitForDisconnect();
 
-    // Note: changing the transport will close and delete the existing one.
-    void set_transport(Transport* transport);
+    // 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
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 0a87598..4da71ca 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -133,7 +133,6 @@
     fb.reset();
 
     if (transport) {
-        transport->Close();
         transport.reset();
     }
 
@@ -188,7 +187,6 @@
         ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
                 << "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
         fb.reset();
-        transport->Close();
         transport.reset();
         printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
         while (UsbStillAvailible())
@@ -249,7 +247,6 @@
     }
 
     if (transport) {
-        transport->Close();
         transport.reset();
     }
 
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 1d30f8b..8fb5a6a 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -304,13 +304,13 @@
 }
 
 TEST_F(Conformance, PartitionInfo) {
-    std::vector<std::tuple<std::string, uint32_t>> parts;
+    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_GT(std::get<1>(p), 0);
+        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;
@@ -340,7 +340,7 @@
     // 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, uint32_t>> parts;
+    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;
@@ -570,7 +570,6 @@
     buf.back() = buf.back() ^ 0x01;
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
-    printf("%02x\n", (unsigned char)buf.back());
     // It can either reject this download or reject it during flash
     if (HandleResponse() != DEVICE_FAIL) {
         EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
@@ -587,14 +586,14 @@
     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, uint32_t>> parts;
+    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, uint32_t>> parts;
+    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) {
@@ -607,7 +606,7 @@
 }
 
 TEST_F(LockPermissions, Erase) {
-    std::vector<std::tuple<std::string, uint32_t>> parts;
+    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) {
@@ -619,7 +618,7 @@
 }
 
 TEST_F(LockPermissions, SetActive) {
-    std::vector<std::tuple<std::string, uint32_t>> parts;
+    std::vector<std::tuple<std::string, uint64_t>> parts;
     EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
 
     std::string resp;
@@ -916,7 +915,7 @@
 
 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, uint32_t>> parts;
+    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) {
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp b/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
index ee510e9..7c595f4 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
+++ b/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp
@@ -12,6 +12,10 @@
                                          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();
 
diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h b/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
index 693f042..8119aea 100644
--- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
+++ b/fastboot/fuzzy_fastboot/usb_transport_sniffer.h
@@ -68,10 +68,11 @@
     };
 
     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;
+    virtual int Close() override final;  // note usage in destructor
     virtual int Reset() override;
 
     const std::vector<Event> Transfers();
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..125182d 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -70,6 +70,11 @@
     fprintf(stderr, "\n");
 }
 
+void Status(const std::string& message) {
+    static constexpr char kStatusFormat[] = "%-50s ";
+    fprintf(stderr, kStatusFormat, message.c_str());
+}
+
 char* xstrdup(const char* s) {
     char* result = strdup(s);
     if (!result) die("out of memory");
diff --git a/fastboot/util.h b/fastboot/util.h
index 9033f93..20be461 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -12,6 +12,8 @@
 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 e3c2c2b..f56043c 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -102,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);
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_priv.h b/fs_mgr/fs_mgr_priv.h
index a347faf..ebc4a0f 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -119,9 +119,13 @@
 
 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_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/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 2b526f6..c4b57c7 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -278,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/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 2015e4d..018c280 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
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index a35cf8e..38842a4 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -192,6 +192,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/reader.cpp b/fs_mgr/liblp/reader.cpp
index 117f5d5..190c650 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -314,7 +314,7 @@
         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) {
         return metadata;
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/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/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 d9a561a..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,7 +33,7 @@
 private:
     int mCommandCount;
     bool mWithSeq;
-    FrameworkCommandCollection *mCommands;
+    std::vector<FrameworkCommand*> mCommands;
     bool mSkipToNextNullByte;
 
 public:
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/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/SocketListener_test.cpp b/libsysutils/src/SocketListener_test.cpp
index fed4546..d6bfd02 100644
--- a/libsysutils/src/SocketListener_test.cpp
+++ b/libsysutils/src/SocketListener_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <sysutils/FrameworkCommand.h>
 #include <sysutils/FrameworkListener.h>
 
 #include <poll.h>
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/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 bb55d1f..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,7 +805,15 @@
               << 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);
@@ -892,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;
             }
@@ -925,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
@@ -969,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;
@@ -983,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()";
     }
@@ -1041,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";
     }
@@ -1069,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.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..0f1badb 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -41,6 +41,7 @@
 #include <atomic>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <android-base/file.h>
@@ -565,23 +566,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
@@ -1472,12 +1464,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..bebcc71 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -557,20 +557,17 @@
 
     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':
@@ -582,8 +579,10 @@
             case 'K':
                 full_size *= 1024;
             /* FALLTHRU */
-            case 'b':
+            case 'B':
                 break;
+            default:
+                ADD_FAILURE() << "Parse error on multiplier: " << size_mult;
         }
         full_consumed = consumed;
         switch (consumed_mult[0]) {
@@ -596,8 +595,10 @@
             case 'K':
                 full_consumed *= 1024;
             /* FALLTHRU */
-            case 'b':
+            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,13 +1230,12 @@
         }
 
         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]) {
@@ -1248,7 +1248,7 @@
                 case 'K':
                     full_size *= 1024;
                 /* FALLTHRU */
-                case 'b':
+                case 'B':
                     break;
             }
             switch (consumed_mult[0]) {
@@ -1261,7 +1261,7 @@
                 case 'K':
                     full_consumed *= 1024;
                 /* FALLTHRU */
-                case 'b':
+                case 'B':
                     break;
             }
             EXPECT_GT(full_size, full_consumed);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1c2ef64..915540e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -306,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
 
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/trusty/keymaster/legacy/trusty_keymaster_device.cpp b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
index ea00a92..afdf43b 100644
--- a/trusty/keymaster/legacy/trusty_keymaster_device.cpp
+++ b/trusty/keymaster/legacy/trusty_keymaster_device.cpp
@@ -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);
     }
 }